Skip to content

Commit 3682c03

Browse files
committed
Fully flush free queues when a transaction starts
Like so many times before, I'm running out of space in the main free queue. This time the reproducer is generic/269 of xfstests. The queue isn't really that full, the problem is that it's very unbalanced. We don't really have any mechanism in place to rebalance btrees, so the easiest way forward is to just empty the free queue entirely whenever possible, that is, when a transaction starts. Of course this means that I no longer hold on to blocks from old transactions, but I never had any real reason to do that. Also, there's no longer any point in retrying fq insertions that fail, since it's just not possible to safely flush any other blocks. Signed-off-by: Ernesto A. Fernández <[email protected]>
1 parent 265f356 commit 3682c03

File tree

1 file changed

+20
-47
lines changed

1 file changed

+20
-47
lines changed

spaceman.c

Lines changed: 20 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -590,11 +590,10 @@ static u64 apfs_free_queue_oldest_xid(struct apfs_node *root)
590590
* apfs_flush_free_queue - Free ip blocks queued by old transactions
591591
* @sb: superblock structure
592592
* @qid: queue to be freed
593-
* @force: flush as much as possible
594593
*
595594
* Returns 0 on success or a negative error code in case of failure.
596595
*/
597-
static int apfs_flush_free_queue(struct super_block *sb, unsigned int qid, bool force)
596+
static int apfs_flush_free_queue(struct super_block *sb, unsigned int qid)
598597
{
599598
struct apfs_nxsb_info *nxi = APFS_NXI(sb);
600599
struct apfs_spaceman *sm = APFS_SM(sb);
@@ -612,19 +611,15 @@ static int apfs_flush_free_queue(struct super_block *sb, unsigned int qid, bool
612611
}
613612

614613
while (oldest) {
615-
u64 sfq_count;
616-
617614
/*
618-
* Try to preserve one transaction here. I don't really know
619-
* what free queues are for so this is probably silly.
615+
* Blocks freed in the current transaction can't be reused
616+
* safely until after the commit, but I don't think there is
617+
* any point in preserving old transacions. I'm guessing the
618+
* official driver keeps multiple transactions going at the
619+
* same time, that must be why they need a free queue.
620620
*/
621-
if (force) {
622-
if (oldest == nxi->nx_xid)
623-
break;
624-
} else {
625-
if (oldest + 1 >= nxi->nx_xid)
626-
break;
627-
}
621+
if (oldest == nxi->nx_xid)
622+
break;
628623

629624
while (true) {
630625
u64 count = 0;
@@ -643,22 +638,6 @@ static int apfs_flush_free_queue(struct super_block *sb, unsigned int qid, bool
643638
}
644639
oldest = apfs_free_queue_oldest_xid(fq_root);
645640
fq->sfq_oldest_xid = cpu_to_le64(oldest);
646-
647-
if (force)
648-
continue;
649-
650-
/*
651-
* Flushing a single transaction may not be enough to avoid
652-
* running out of space in the ip, but it's probably best not
653-
* to flush all the old transactions at once either. We use a
654-
* harsher version of the apfs_transaction_need_commit() check,
655-
* to make sure we won't be forced to commit again right away.
656-
*/
657-
sfq_count = le64_to_cpu(fq->sfq_count);
658-
if (qid == APFS_SFQ_IP && sfq_count * 6 <= le64_to_cpu(sm_raw->sm_ip_block_count))
659-
break;
660-
if (qid == APFS_SFQ_MAIN && sfq_count <= APFS_TRANS_MAIN_QUEUE_MAX - 200)
661-
break;
662641
}
663642

664643
fail:
@@ -787,12 +766,16 @@ int apfs_read_spaceman(struct super_block *sb)
787766
goto fail;
788767
}
789768

790-
err = apfs_flush_free_queue(sb, APFS_SFQ_IP, false /* force */);
769+
/*
770+
* We flush free queues whole when each transaction begins, to make it
771+
* harder for the btrees to become too unbalanced.
772+
*/
773+
err = apfs_flush_free_queue(sb, APFS_SFQ_IP);
791774
if (err) {
792775
apfs_err(sb, "failed to flush ip fq");
793776
goto fail;
794777
}
795-
err = apfs_flush_free_queue(sb, APFS_SFQ_MAIN, false /* force */);
778+
err = apfs_flush_free_queue(sb, APFS_SFQ_MAIN);
796779
if (err) {
797780
apfs_err(sb, "failed to flush main fq");
798781
goto fail;
@@ -926,8 +909,7 @@ static inline int apfs_chunk_mark_free(struct super_block *sb, char *bitmap,
926909
* @count: number of consecutive blocks to free
927910
*
928911
* Same as apfs_free_queue_insert_nocache(), except that this one can also fail
929-
* with -EAGAIN if there is no room for the new record, so that the caller can
930-
* flush the queue and retry.
912+
* with -ENOSPC if there is no room for the new record.
931913
*/
932914
static int apfs_free_queue_try_insert(struct super_block *sb, u64 bno, u64 count)
933915
{
@@ -978,7 +960,7 @@ static int apfs_free_queue_try_insert(struct super_block *sb, u64 bno, u64 count
978960
if (node_count == node_limit) {
979961
needed_room = sizeof(raw_key) + (ghost ? 0 : sizeof(raw_val));
980962
if (!apfs_node_has_room(query->node, needed_room, false /* replace */)) {
981-
err = -EAGAIN;
963+
err = -ENOSPC;
982964
goto fail;
983965
}
984966
}
@@ -1020,25 +1002,16 @@ static int apfs_free_queue_try_insert(struct super_block *sb, u64 bno, u64 count
10201002
*/
10211003
int apfs_free_queue_insert_nocache(struct super_block *sb, u64 bno, u64 count)
10221004
{
1023-
struct apfs_spaceman *sm = APFS_SM(sb);
10241005
unsigned int qid;
10251006
int err;
10261007

10271008
err = apfs_free_queue_try_insert(sb, bno, count);
1028-
if (err == -EAGAIN) {
1029-
qid = apfs_block_in_ip(sm, bno) ? APFS_SFQ_IP : APFS_SFQ_MAIN;
1030-
err = apfs_flush_free_queue(sb, qid, true /* force */);
1031-
if (err) {
1032-
apfs_err(sb, "failed to flush fq to make room");
1033-
return err;
1034-
}
1035-
err = apfs_free_queue_try_insert(sb, bno, count);
1009+
if (err == -ENOSPC) {
1010+
qid = apfs_block_in_ip(APFS_SM(sb), bno) ? APFS_SFQ_IP : APFS_SFQ_MAIN;
1011+
apfs_alert(sb, "free queue (%u) seems full - bug!", qid);
1012+
err = -EFSCORRUPTED;
10361013
}
10371014
if (err) {
1038-
if (err == -EAGAIN) {
1039-
apfs_alert(sb, "failed to make room in fq - bug!");
1040-
err = -EFSCORRUPTED;
1041-
}
10421015
apfs_err(sb, "fq insert failed (0x%llx-0x%llx)", bno, count);
10431016
return err;
10441017
}

0 commit comments

Comments
 (0)