When resizing a gmirror gives you a hard time

April 29, 2026, 9:19 a.m.

I recently ran into an annoying problem on one of my FreeBSD machines: resizing a gmirror. Spoiler: it stops being annoying once I remember to enable the human built-in "thinking" feature.

To keep the example simple, imagine a setup with three partitions across two disks: system (UFS), swap, and data (ZFS). The system partitions are mirrored with GEOM Mirror.

Yes, this is not an ideal layout. It is also not the setup I actually use. It is just a compact way to illustrate the problem.

To play with the idea without touching real disks, I built a small example with memory disks (md).

First, I created two 10 GiB memory-backed disks and partitioned them in the same way:

$ mdconfig -a -t swap -s 10g -u 1
$ mdconfig -a -t swap -s 10g -u 2
$ gpart create -s gpt md1
$ gpart create -s gpt md2
$ gpart add -t freebsd-ufs -s 1g md1
$ gpart add -t freebsd-swap -s 4g md1
$ gpart add -t freebsd-zfs md1
$ gpart add -t freebsd-ufs -s 1g md2
$ gpart add -t freebsd-swap -s 4g md2
$ gpart add -t freebsd-zfs md2
$ gmirror label -vb round-robin system /dev/md1p1 /dev/md2p1

$ gpart show
=>      40  20971440  md1  GPT  (10G)
        40   2097152    1  freebsd-ufs  (1.0G)
   2097192   8388608    2  freebsd-swap  (4.0G)
  10485800  10485680    3  freebsd-zfs  (5.0G)

=>      40  20971440  md2  GPT  (10G)
        40   2097152    1  freebsd-ufs  (1.0G)
   2097192   8388608    2  freebsd-swap  (4.0G)
  10485800  10485680    3  freebsd-zfs  (5.0G)

$ gmirror status
           Name    Status  Components
mirror/system  COMPLETE  md1p1 (ACTIVE)
                         md2p1 (ACTIVE)

Now for the problem. The system partition has run out of space. The plan was simple: remove the swap partition and grow the system partition to use the newly available space.

I successfully resized the GPT partition of the mirror.

$ gpart delete -i 2 md1
$ gpart delete -i 2 md2
$ gpart resize -i 1 md1
$ gpart resize -i 1 md2

Then I tried to resize the gmirror itself with gmirror resize -s. The -s flag expects a size in sectors, so I checked the partition size with diskinfo.

$ diskinfo -v /dev/md1p1
/dev/md1p1
    512             # sectorsize
    5368709120      # mediasize in bytes (5.0G)
    10485760        # mediasize in sectors
    0               # stripesize
    20480           # stripeoffset
    652             # Cylinders according to firmware.
    255             # Heads according to firmware.
    63              # Sectors according to firmware.
    Yes             # TRIM/UNMAP support
    Unknown         # Rotation rate in RPM
$ gmirror resize -s 10485760 system
gmirror: Provider md2p1 is too small.

I used the partition size reported by diskinfo, and the command failed. According to gmirror, the provider was too small.

At that point, I asked an LLM for ideas. The LLM confidently decided to become a storage expert. It suggested that the GEOM layer might not have updated the provider size yet. The theory was that gmirror had cached the old provider size and the new size was not being propagated correctly. It even pointed me at the relevant parts of the GEOM code. That was the fun part. It was wrong in a fairly sophisticated way.

That was a reasonable theory. It would also have been a fun kernel bug. It was exactly the sort of explanation that sounds convincing enough to steal an afternoon. So I read the code, lost a couple of hours, and then did the obvious sanity check:

$ gmirror list
Geom name: system
State: COMPLETE
Components: 2
Balance: load
Slice: 4096
Flags: NONE
GenID: 0
SyncID: 1
ID: 2432106634
Type: MANUAL
Providers:
1. Name: mirror/system
   Mediasize: 1073741824 (1.0G)
   Sectorsize: 512
   Mode: r0w0e0
Consumers:
1. Name: md1p1
   Mediasize: 5368709120 (5.0G)
   Sectorsize: 512
   Stripesize: 0
   Stripeoffset: 20480
   Mode: r1w1e1
   State: ACTIVE
   Priority: 0
   Flags:
   GenID: 0
   SyncID: 1
   ID: 1782260984
2. Name: md2p1
   Mediasize: 5368709120 (5.0G)
   Sectorsize: 512
   Stripesize: 0
   Stripeoffset: 20480
   Mode: r1w1e1
   State: ACTIVE
   Priority: 1
   Flags:
   GenID: 0
   SyncID: 1
   ID: 2354113277

gmirror was already reporting both providers as 5.0G. So the resize information had propagated correctly after all.

The real issue was much simpler: I had used the exact GPT partition size and forgotten that gmirror stores metadata at the end of the provider.

In other words, by resizing to the full partition size, I had left no room for the final sector that gmirror needs for its metadata.

That was the whole problem.

The fix was to either omit the -s parameter entirely or subtract one sector from the size passed to gmirror resize.

Once I did that, the resize worked exactly as expected.

# gmirror resize -s 10485759 system
# gmirror list
...
Providers:
1. Name: mirror/system
   Mediasize: 5368708608 (5.0G)
   Sectorsize: 512
   Mode: r0w0e0
...

It was a good reminder that LLMs are very good at generating plausible debugging theories. Sometimes those theories are even useful because they push me to inspect the right subsystem. But plausible is not the same thing as correct. In this case, the answer was not delayed size propagation or some hidden kernel bug. It was one missing sector at the end of the disk.

Human admins still win this round.