数年運用している FreeBSD を zfs raid1 にしたい。
元はSSD1台。追加したSSDは同じ型番。
# camcontrol devlist
<Samsung SSD 850 PRO 512GB EXM02B6Q> at scbus6 target 0 lun 0 (pass5,ada4) # new ssd
<Samsung SSD 850 PRO 512GB EXM02B6Q> at scbus11 target 0 lun 0 (pass10,ada9) # old ssd
他の環境では何度も成功している gpart backup | gpart restore が通らない。
# gpart backup /dev/ada9 | gpart restore -F /dev/ada4
gpart: start '34': Invalid argument
どうやら、元は first:34 先は first:40 のようだ。これでは上手くいく筈がない。
# gpart backup /dev/ada9
GPT 128
1 freebsd-boot 34 128
2 freebsd-zfs 162 1000215021
# gpart list ada9
Geom name: ada9
modified: false
state: OK
fwheads: 16
fwsectors: 63
last: 1000215182
first: 34
entries: 128
scheme: GPT
Providers:
1. Name: ada9p1
Mediasize: 65536 (64K)
Sectorsize: 512
Stripesize: 4096
Stripeoffset: 1024
Mode: r0w0e0
efimedia: HD(1,GPT,83e479d0-f41b-11e4-92db-0025229f7519,0x22,0x80)
rawuuid: 83e479d0-f41b-11e4-92db-0025229f7519
rawtype: 83bd6b9d-7f41-11dc-be0b-001560b84f0f
label: (null)
length: 65536
offset: 17408
type: freebsd-boot
index: 1
end: 161
start: 34
2. Name: ada9p2
Mediasize: 512110090752 (477G)
Sectorsize: 512
Stripesize: 4096
Stripeoffset: 1024
Mode: r1w1e2
efimedia: HD(2,GPT,868c6c71-f41b-11e4-92db-0025229f7519,0xa2,0x3b9e11ed)
rawuuid: 868c6c71-f41b-11e4-92db-0025229f7519
rawtype: 516e7cba-6ecf-11d6-8ff8-00022d09712b
label: (null)
length: 512110090752
offset: 82944
type: freebsd-zfs
index: 2
end: 1000215182
start: 162
Consumers:
1. Name: ada9
Mediasize: 512110190592 (477G)
Sectorsize: 512
Stripesize: 4096
Stripeoffset: 0
Mode: r1w1e3
# gpart list ada4
gpart: Class 'PART' does not have an instance named 'ada4'.
# gpart create -s GPT ada4
ada4 created
# gpart list ada4
Geom name: ada4
modified: false
state: OK
fwheads: 16
fwsectors: 63
last: 1000215175
first: 40
entries: 128
scheme: GPT
Consumers:
1. Name: ada4
Mediasize: 512110190592 (477G)
Sectorsize: 512
Stripesize: 4096
Stripeoffset: 0
Mode: r0w0e0
FreeBSD 12 では start:34 は作れないのだろうか。
# freebsd-version -ku
12.0-RELEASE-p10
12.0-RELEASE-p11
このコミットが影響していて、実質 34 -> 40 がデフォルトになったように思える。
https://github.com/freebsd/freebsd/commit/eff424dd1339357d9cf9921b472de2138aa48d31
変更は出来ないように思える。
それでは既存の freebsd-zfs に足すミラーは作れるのだろうか。
どうやら試行錯誤してみると、既存の pool より小さくても attach できることが分かった。
# gpart backup /dev/ada9
GPT 128
1 freebsd-boot 34 128
2 freebsd-zfs 162 1000215021 <- これより小さくても attach 出来た
# gpart backup /dev/ada4
GPT 128
1 freebsd-zfs 162 998500000 <- attach 可
# gpart backup /dev/ada4
GPT 128
1 freebsd-zfs 162 998000000 <- attach 不可
# zpool attach zroot /dev/oldp2 /dev/newp1
cannot attach /dev/newp1 to /dev/oldp2: device is too small
なんとかなりそうだ。
だが、この境界はどうやって決まっているのだろうか。
# perl -nle 'print if /Get the minimum/../^}/' sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev.c
* Get the minimum allocatable size. We define the allocatable size as
* the vdev's asize rounded to the nearest metaslab. This allows us to
* replace or attach devices which don't have the same physical size but
* can still satisfy the same number of allocations.
*/
uint64_t
vdev_get_min_asize(vdev_t *vd)
{
vdev_t *pvd = vd->vdev_parent;
/*
* If our parent is NULL (inactive spare or cache) or is the root,
* just return our own asize.
*/
if (pvd == NULL)
return (vd->vdev_asize);
/*
* The top-level vdev just returns the allocatable size rounded
* to the nearest metaslab.
*/
if (vd == vd->vdev_top)
return (P2ALIGN(vd->vdev_asize, 1ULL << vd->vdev_ms_shift));
/*
* The allocatable space for a raidz vdev is N * sizeof(smallest child),
* so each child must provide at least 1/Nth of its asize.
*/
if (pvd->vdev_ops == &vdev_raidz_ops)
return ((pvd->vdev_min_asize + pvd->vdev_children - 1) /
pvd->vdev_children);
return (pvd->vdev_min_asize);
}
模すと
# zdb -C zroot | ack 'asize|shift'
metaslab_shift: 32
ashift: 9
asize: 512105119744
return (P2ALIGN(vd->vdev_asize, 1ULL << vd->vdev_ms_shift));
512105119744 32
# perl -E 'say 1<<32'
4294967296
# perl -E 'say 512105119744/4294967296'
119.233764648438
# perl -E 'say 4294967296*119'
511101108224
# perl -E 'say 4294967296*119/512'
998244352
998244352 は上の 998000000 〜 998500000 の間にある。
もう少しコードを読んでみると。
# sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/vdev_impl.h
typedef struct vdev_label {
char vl_pad1[VDEV_PAD_SIZE]; /* 8K */
char vl_pad2[VDEV_PAD_SIZE]; /* 8K */
vdev_phys_t vl_vdev_phys; /* 112K */
char vl_uberblock[VDEV_UBERBLOCK_RING]; /* 128K */
} vdev_label_t; /* 256K total */
/*
* vdev_dirty() flags
*/
#define VDD_METASLAB 0x01
#define VDD_DTL 0x02
/* Offset of embedded boot loader region on each label */
#define VDEV_BOOT_OFFSET (2 * sizeof (vdev_label_t))
/*
* Size of embedded boot loader region on each label.
* The total size of the first two labels plus the boot area is 4MB.
*/
#define VDEV_BOOT_SIZE (7ULL << 19) /* 3.5M */
/*
* Size of label regions at the start and end of each leaf device.
*/
#define VDEV_LABEL_START_SIZE (2 * sizeof (vdev_label_t) + VDEV_BOOT_SIZE)
#define VDEV_LABEL_END_SIZE (2 * sizeof (vdev_label_t))
と VDEV_LABEL_START_SIZE + VDEV_LABEL_END_SIZE あたりから
# perl -E 'say 998244352 + ((256*1024*2)+(7<<19))/512 + (256*1024*2)/512'
998253568
境界は 998253568 だろうか。合っているようだ。
# gpart backup /dev/ada4
GPT 128
1 freebsd-zfs 162 998253568 <- attach 可
# gpart backup /dev/ada4
GPT 128
1 freebsd-zfs 162 998253567 <- attach 不可
# gpart backup と境界の差
# perl -E 'say 1000215021 - 998253568'
1961453
# perl -E 'say 1961453*512/1024/1024'
957.74072265625 # gpart list の size より 約957メガ 小さくても attach 可能
結論
1 上のコミット後は gpart create -s GPT で first:34 のものは作れず first:40 になる。
2 zfs attach zmirror oldp newp する場合、newp は oldp の asize より小さくできる場合がある。