2014年4月7日

パーティションテーブルのオンライン編集

Linux動作中に、mountなどで利用しているディスクのパーティションテーブルを変更するときにエラーになったりならなかったりするはなし。
 
パーティションテーブルの書き換え自体はいきなりブロックデバイスに書くのでいつでもできます。しかしカーネルが保持しているパーティション情報の更新は失敗するケースがあります。パーティション情報の更新はデバイスにたいするioctlでおこない、対応する手法によって失敗する条件が変わります。

ioctl BLKRRPART でパーティションを読み直しさせると、既に利用しているデバイスの場合はEBUSYで失敗します。

つかうがわ
util-linux-ng-2.17.2/fdisk/fdisk.c

                printf(_("Calling ioctl() to re-read partition table.\n"));
                i = ioctl(fd, BLKRRPART);

つかわれるがわ
linux-2.6.32-71.el6.x86_64/fs/partitions/check.c

int rescan_partitions(struct gendisk *disk, struct block_device *bdev)
{
        struct disk_part_iter piter;
        struct hd_struct *part;
        struct parsed_partitions *state;
        int p, highest, res;

        if (bdev->bd_part_count)
                return -EBUSY;
ioctl BLKPG でパーティションを明示して追加させると、既に利用しているパーティションと競合する場合はEBUSYで失敗します

つかうがわ
util-linux-ng-2.17.2/partx/partx.c
                                pt.pno = lower+j;
                                pt.start = 512 * (long long) slices[j].start;
                                pt.length = 512 * (long long) slices[j].size;
                                pt.devname[0] = 0;
                                pt.volname[0] = 0;
                                a.op = BLKPG_ADD_PARTITION;
                                a.flags = 0;
                                a.datalen = sizeof(pt);
                                a.data = &pt;
                                if (ioctl(fd, BLKPG, &a) == -1) {
                                    perror("BLKPG");

つかわれるがわ
linux-2.6.32-431.el6.x86_64/block/ioctl.c
                        /* overlap? */
                        disk_part_iter_init(&piter, disk,
                                            DISK_PITER_INCL_EMPTY);
                        while ((part = disk_part_iter_next(&piter))) {
                                if (!(start + length <= part->start_sect ||
                                      start >= part->start_sect + part->nr_sects)) {
                                        disk_part_iter_exit(&piter);
                                        mutex_unlock(&bdev->bd_mutex);
                                        return -EBUSY;
                                }
                        }

再起動せずにパーティションを追加してそのパーティションをブロックデバイスとして見せたいシチュエーションでは、BLKPGを使うpartxなどでカーネルにパーティション追加の通知をすると既存のパーティションと重なっていなければ利用可能です。

さらに異常なシチュエーションで、既存のパーティションと重なっているような場合であっても、devicemapperで適当なマッピングをパーティション情報から作ってくれる kpartx を利用すればなんとでもなったりします……。