Re: [PATCH] ocfs2: fix cached cluster count after suballocator reclaim

From: Matthias Goergens

Date: Wed Aug 05 2026 - 03:59:42 EST


Hello Heming,

On Wed, Aug 05, 2026 at 03:38:05PM +0800, Heming Zhao wrote:
> Could you share your test case, I want to reproduce the issue and verify the
> fix.

Sure. The test runs in a QEMU guest on a snapshot-only virtio disk (the
host image is never modified). The image is a clean 256 MiB single-node
local OCFS2 filesystem made with ocfs2-tools 1.8.9:

truncate --size=256M accounting.ocfs2
mkfs.ocfs2 -F -N 1 -M local accounting.ocfs2

The guest runs the small program below: mount with
heartbeat=none,localflocks, create 10,000 empty files, sync, then unlink
them all, sync. That drives four suballocator reclaims on this image.

To observe the accounting I used a temporary diagnostic, added immediately
after the ip_clusters update in _ocfs2_reclaim_suballoc_to_main() (not part
of the submitted patch):

pr_warn("OCFS2_RECLAIM_ACCOUNTING old=%u disk=%u cache=%u cpg=%u\n",
tmp_used, le32_to_cpu(fe->i_clusters),
OCFS2_I(alloc_inode)->ip_clusters, le16_to_cpu(cl->cl_cpg));

On the stock kernel the first reclaim already leaves the cache at 512
while the disk holds 2048, and later reclaims underflow the cache. With
the patch the cache matches the disk at every reclaim: 2048, 1536, 1024,
512.

The guest reproducer (statically linked, run as init in a minimal
initramfs):

#define _GNU_SOURCE

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <unistd.h>

#define FILE_LIMIT 10000

int main(void)
{
int created = 0;

if (mkdir("/mnt", 0777) && errno != EEXIST) {
perror("mkdir /mnt");
return 1;
}
if (mount("/dev/vda", "/mnt", "ocfs2", MS_NODIRATIME,
"heartbeat=none,localflocks")) {
perror("mount /dev/vda");
return 1;
}
if (chdir("/mnt")) {
perror("chdir /mnt");
return 1;
}

puts("ACCOUNTING_TEST: mounted; creating files");
for (int i = 0; i < FILE_LIMIT; i++) {
char path[32];
int fd;

snprintf(path, sizeof(path), "inode-%05d", i);
fd = open(path, O_CREAT | O_EXCL | O_RDWR, 0600);
if (fd < 0) {
printf("ACCOUNTING_TEST: create failed at %d: %s\n",
i, strerror(errno));
break;
}
close(fd);
created++;
}
sync();

printf("ACCOUNTING_TEST: unlinking %d files\n", created);
for (int i = created - 1; i >= 0; i--) {
char path[32];

snprintf(path, sizeof(path), "inode-%05d", i);
if (unlink(path)) {
printf("ACCOUNTING_TEST: unlink failed at %d: %s\n",
i, strerror(errno));
return 1;
}
}
sync();
puts("ACCOUNTING_TEST: complete");
return 0;
}

I also have the full harness (image builder, initramfs builder, QEMU
runner with a log-checking mode) if you want it; happy to post it or send
it privately.

Thanks,
Matthias