2014年5月28日

アトミック・カーネルスレッドと言うものがあるらしい

BTRFSのディスクからの読み込み終了時の関数 btrfs_readpage_end_io_hook() をいじっている。

目的とした機能が動作して最終テストを行っていたら、dmesg が下のように文句を言っている。

BUG: scheduling while atomic: btrfs-endio-1/1157/0x00000001
Modules linked in:
Pid: 1157, comm: btrfs-endio-1 Not tainted 3.0.101.RNx86_64.2.2+ #121
Call Trace:
 [<ffffffff8887d4bd>] ? __schedule_bug+0x54/0x58
 [<ffffffff8888712b>] ? __schedule+0x59b/0xa30
 [<ffffffff88398299>] ? put_dec+0x59/0x60
 [<ffffffff880d0be4>] ? add_partial+0x24/0x80
 [<ffffffff880d240e>] ? deactivate_slab+0x4e/0xf0
 [<ffffffff88774849>] ? __alloc_skb+0x99/0x270
 [<ffffffff8888768a>] ? schedule+0x3a/0x50
 [<ffffffff88042e6d>] ? sys_sched_yield+0x3d/0x50
 [<ffffffff888878bf>] ? yield+0x2f/0x40
 [<ffffffff88799ea0>] ? netlink_broadcast_filtered+0x330/0x480
 [<ffffffff8879a009>] ? netlink_broadcast+0x19/0x20
 [<ffffffff883ad39e>] ? nla_put+0x2e/0x40
 [<ffffffff883488af>] ? mdcsrepair_netlink+0x46f/0x580
 [<ffffffff882e6711>] ? btrfs_readpage_end_io_hook+0x1d1/0x3a0
 [<ffffffff882fd64e>] ? end_bio_extent_readpage+0x12e/0x9c0
 [<ffffffff88106fe8>] ? bio_endio+0x18/0x30
 [<ffffffff882d864f>] ? end_workqueue_fn+0x3f/0x50
 [<ffffffff8830d3f0>] ? worker_loop+0x140/0x500
 [<ffffffff8830d2b0>] ? btrfs_queue_worker+0x300/0x300
 [<ffffffff88063a09>] ? kthread+0x89/0x90
 [<ffffffff8888aa74>] ? kernel_thread_helper+0x4/0x10
 [<ffffffff88063980>] ? kthread_worker_fn+0x140/0x140
 [<ffffffff8888aa70>] ? gs_change+0xb/0xb


調べてみると、in_atomic_preempt_off() が真になっていることが直接の原因。どうもこのワーカースレッドはプリエンプションだけでなくスケジューリングも禁止しており、スレッドとは言え、割り込み処理のようにCPUを手放さずにスレッド終了まで実行しなければならないようだ。このコールスタックでは nla_put()(Netlinkのメッセージ構築関数)がスケジュールしてしまうためにバグとしてスタックダンプをしている。実際にはスタックダンプがあっても目的の機能は実行されて問題がないのだが、そこはホレ、やはりこういうものは出現させるべきではない。

解決策としては、 nla_put()をやめる訳には行かず、またこのスレッドをノン・アトミックにするのもはばかれるので、一計を案じてアトミックでない通常のスレッドを別に走らせてそこに必要なデータを渡してnla_put()以降の処理を依頼することにした。ここでは無限ループのサービススレッドでうまく行ったが、1回限りのワーカースレッドでもうまく行くかどうかは試していない。

2014年5月13日

古いラップトップの延命策 - 802.11n と HDDからSSDへの換装

小生が家でメインに使っているPCはDELLのXPS M1330というラップトップ。7年前に、当時働いていた会社がDELLに買収されたときに個人用として各従業員がタダで貰った。Core 2 Duo T7500 2.2GHz の CPU でメモリは 4GB、160GB の HDD。

Windows Vista(Business、32bit)と言うWindows8ほどではないものの最悪に近いOSであることと画面が1280x800と少々小さめであることを除けば、最近のMacほどはないものの、ウェッジ型の薄く見える形状で気に入っている。毎年のバケーションにもいろいろな国に持って行っており、バッテリーは3代目だ。

気に入っているとは言え、このラップトップは、1年以上使用されているすべてのPCに共通する問題を抱えている。

お・そ・い
今年は家族メンバーのPCのアップグレードが2件予定されており(1件は実施済み)、新しいラップトップに買い換える予算がない。

そこで、最小限の費用での延命策を検討した結果、採用したのが以下の2項目。
  • WiFi の 802.11g から 802.11n への変更
  • HDDをSSDに換装
WiFi の変更は、手元に小型の802.11n 小型USBドングルがあったので試しに入れてみた。あまり期待はしていなかったのだが、実際に体感できる速度の向上(30~40%?)があったのは儲けもの。単価$10前後で実施できる。本当は内臓の WiFi が交換できれば、USB端子も消費せずに済むのだが、ないものねだりをしても仕方がない。

ドングルを挿して、付属のドライバをインストールする。コントロールパネルから内臓の WiFi を禁止してお終い。あ、MACアドレスで固定のDHCPを行っている場合は、当然DHCPサーバのMACアドレスを変更しなければならない。

ただし、ドングルにもいろいろあるようで、最初はRaspberry Pi を買った時に付属してきた(Linuxでサポートされていない、したがって役に立たない)Realtek RTL8188CU を使っていたが、どうも不安定で、20分ごとにデバイスが再起動していた。Raspberry Piで実績のあったEdimaxに変更したら随分安定になった。

インターネットに使用しているケーブルモデムは、最近スピードサイトで測ったら60Mbps程度。家の中のLANも100Mbpsから1Gbpsに順次交換している。WiFiも速くすると効果的と判った。

HDDからSSDへの換装は、依然SSDで否定的な経験をしたので躊躇していたが、もうそろそろ時期と思い、再び実践。結果は
は・や・い
の一言。まるで購入時のあのサクサクさが戻ってきたようだ。

使用したSSDは SanDisk の 256GB。WDの普及版1TB HDD と比べるとバイトあたり単価は約8倍。
  • SSD 256GB $120
  • HDD  1TB $60
将来はこの価格差が縮む可能性はあるか?

SSDの容量は内部チップの面積(数)に正比例し、これが価格の大部分を占めるが、HDDでは、プラッタを除く、ハウジングやアクチュエータ、ボイスコイル、PC基盤といった主要部品は容量にあまり依存しないので、容量と価格は正比例しないと思う。だから、単純な価格比にはならないだろう。

HDDからSSDへの換装は、マザーボードに空きSATAコネクタが2個あるLinuxを使うと至極簡単。

まず、HDDより少し大きめの容量のSSDを買って来る。我が家の場合は160GB -> 256GBとして、SanDisk のUltra Plus にした。

データのバックアップは言われなくてもちゃんと取ること。小生はGenie9というWindowsのバックグラウンド型バックアッププログラムを使っているので、新しいファイルシステムに変更があると即座にその分がバックアップされるようになっている。

ラップトップをシャットダウン(「スリープ」はだめ)して、バッテリを取り去り、HDDを外す。Linux マシンにHDDとSSDを装着して起動。以下のコマンドを実行して数十分待てばディスクの複製が完了(HDDが/dev/sdb、SSDが/dev/sdcの場合)。
[root@linux ~]# dd if=/dev/sdb of=/dev/sdc bs=16M

HDDのあったところにSSDを装着して蓋を閉め、バッテリを戻してラップトップを起動すると、もしかしたらBIOSが何か言ってくるかもしれないが、すべて肯定的に返事をすれば、きっと立ち上がり時間が1/10になっていることに驚くはず。

元のHDDより大容量のSDDを使ったので、ディスクの後ろの方が余る。小生が実践した換装では、86GBほど余ってしまった。WindowsでCドライブしか使っていなければ、新たなパーティションを作ってDドライブとして使うのが一番簡単。小生の場合はちょっと複雑で、Cの後ろにバックアップの対象としないVMWareの仮想ドライブファイルを格納するVドライブが既にある(バックアップはVMWareのゲストOS内で実行)。新たなパーティションでやたらドライブ数を増やしても仕方がないので、この新しいパーティションはVドライブをコピーしてきて、以前のVドライブの場所はCドライブを延長しようかと考えている。ま、この辺は各自でどうぞ。

これで7年前のラップトップが快調に甦った。あと3年ぐらいは使えそうな感じがする。

大抵の突然のわけの分からない不具合はSELINUXを禁止すると治る

今までに何回わけの分からない不具合がこれだったか…。

ほとんどの場合は、POSIXのファイルパーミッションでは不可思議な現象に関連していたが、今回はFedora 20で新しいサーバを構築中に起こった。

下は yum  実行時に起きたわけの分からないエラーメッセージ。リポジトリサーバがダウンしていたのかと思い半日待ったが埒があかない。「まさか」と思いつつ /etc/selinux/config で「SELINUX=disabled」にしたらあっけなく治った。

[root@mill3 ~]# yum makecache
Loaded plugins: langpacks, refresh-packagekit


 One of the configured repositories failed (Unknown),
 and yum doesn't have enough cached data to continue. At this point the only
 safe thing yum can do is fail. There are a few ways to work "fix" this:

     1. Contact the upstream for the repository and get them to fix the problem.

     2. Reconfigure the baseurl/etc. for the repository, to point to a working
        upstream. This is most often useful if you are using a newer
        distribution release than is supported by the repository (and the
        packages for the previous distribution release still work).

     3. Disable the repository, so yum won't use it by default. Yum will then
        just ignore the repository until you permanently enable it again or use
        --enablerepo for temporary usage:

            yum-config-manager --disable <repoid>

     4. Configure the failing repository to be skipped, if it is unavailable.
        Note that yum will try to contact the repo. when it runs most commands,
        so will have to try and fail each time (and thus. yum will be be much
        slower). If it is a very temporary problem though, this is often a nice
        compromise:

            yum-config-manager --save --setopt=<repoid>.skip_if_unavailable=true

Cannot retrieve metalink for repository: fedora/20/x86_64. Please verify its path and try again

SELINUX は本当に質(たち)が悪い。

2014年5月10日

Fedora 20: em1 ではなく eth0 にしたい

我が家のサーバ群は4年前に構築した Fedora 13(以下FC13)。最近のOpenSSLのセキュリティホールなどの記事を読むに連れ、そろそろ更新の時期と考え、まずはセキュリティに一番関係するファイアウォールのアップグレードから始めた。32ビットから64ビットへの移行も大きな動機。
 
選んだのは現在最新のFC20。世の中はUbuntu系が急速に普及しているが、Ubuntu系は経験的にワークステーションにはよくても、systemd採用以前のバージョンはいろいろなサービスの直交性が低い印象があり、また今まで延々とFedoraで作ってきたいろいろな自家製サービスやツールを考えると、ここ暫くはFedora系に固執するのが安全という判断。

幸いなことに、あのWindows8もどきの最悪なデスクトップ(Gnome3)を嫌う多くの人に支持されたGnome2の直径子孫のMATEのスピンがあるのでそれを使うことにした。

前置きが長くなったが、取りあえず古いPC上に実験的にMATEをインストールする。インストーラのanacondaが使いにくくなったなど色々あるが、それらは別稿で書くとして、一番驚いたのは、ネットワーク・デバイス名が「ethX」から「emY」に変更されていること。ifconfigの出力フォーマットの変更(こちらの方が影響の可能性が大きそう)もさることながら、実害の可能性は低いと思うが、もしかしたら自家製のツールのもうとっくに忘れた部分でわけの分からない壊れ方をするかもしれない。

[root@mill3 ~]# ifconfig
em1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet6 fe80::6670:2ff:fe11:29eb  prefixlen 64  scopeid 0x20<link>
        ether 64:70:02:11:29:eb  txqueuelen 1000  (Ethernet)
        RX packets 139  bytes 21434 (20.9 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 8  bytes 648 (648.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

ぐぐってみると、どうもこの仕様はFC15あたりから導入されたらしいが、「emY から ethX に戻したい」と言う同じ悩みを抱えた人は大勢いるらしく、結構な数の記事がヒットする。それらを色々試してみるがうまく行かない。結局たどり着いたのはこちらの記事

これによると、以下の2ヶ所を手当てすればよい。
  • grub の設定ファイルのカーネルへの引数
  • /etc/sysconfig/network-scripts/ifcfg-eth* の作成
  • udev のルールファイルを /etc/udev/rules.d/60-net.rules でオーバライド
他の様々な記事では、udev のルールファイルを変更するとよいと書いてあるが、ルールファイルを変更(と言うよりオーバライド)しただけでは何も変化はなかった。

まず、カーネルのブート時にemYへのリネームを禁止する引数を渡す。

/boot/grub/grub.conf と言うgrub独自言語のファイルが /boot/grub2/grub.cfg と言うシェルスクリプトに変わってしまったことにも驚いたが、とにかく集めた情報を基に、以下のパラメタ(net.ifnames=0 biosdevname=0)をカーネルの起動時コマンドラインに追加する。

### BEGIN /etc/grub.d/10_linux ###
menuentry 'Fedora (3.14.2-200.fc20.x86_64) 20 (Heisenbug)' --class fedora --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-3.11.10-301.fc20.x86_64-advanced-4964450e-bb98-4b4e-ac6f-cfd3c0f272c6' {
        load_video
        set gfxpayload=keep
        insmod gzio
        insmod part_gpt
        insmod part_gpt
        insmod diskfilter
        insmod mdraid1x
        insmod ext2
        set root='mduuid/2978f1951a5f27f2fa616dc44e7225c6'
        if [ x$feature_platform_search_hint = xy ]; then
          search --no-floppy --fs-uuid --set=root --hint='mduuid/2978f1951a5f27f2fa616dc44e7225c6'  4dd4ee0a-f0ce-48a1-894d-e329ee6ac3da
        else
          search --no-floppy --fs-uuid --set=root 4dd4ee0a-f0ce-48a1-894d-e329ee6ac3da
        fi
        linux   /vmlinuz-3.14.2-200.fc20.x86_64 root=UUID=4964450e-bb98-4b4e-ac6f-cfd3c0f272c6 ro rd.md.uuid=2978f195:1a5f27f2:fa616dc4:4e7225c6 rd.md.uuid=f52a49f7:ae3bca96:5f1530ab:cd3bd27c rd.md.uuid=d085c9c0:4d36caf6:ec19a007:ad1e1c74 vconsole.font=latarcyrheb-sun16  rhgb quiet LANG=en_US.UTF-8 net.ifnames=0 biosdevname=0
        initrd /initramfs-3.14.2-200.fc20.x86_64.img
}
以下略


上記記事によれば、FC18までは「biosdevname=0」だけでよかったが、FC19以降ではこれだけだと以下のようなドライバの付けたデバイス名になってしまう。

[root@mill3 ~]# ifconfig
enp1s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet6 fe80::6670:2ff:fe11:29eb  prefixlen 64  scopeid 0x20<link>
        ether 64:70:02:11:29:eb  txqueuelen 1000  (Ethernet)
        RX packets 139  bytes 21434 (20.9 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 8  bytes 648 (648.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

/boot/grub2/grub.cfg は新しいカーネルがインストールされる度に書き換えられてしまうので、この変更を将来も自動的に行うためには、/etc/default/grub にこれらを追加しておく。

GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/system-release)"
GRUB_DEFAULT=saved
GRUB_DISABLE_SUBMENU=true
GRUB_TERMINAL_OUTPUT="console"
GRUB_CMDLINE_LINUX="rd.md.uuid=2978f195:1a5f27f2:fa616dc4:4e7225c6 rd.md.uuid=f52a49f7:ae3bca96:5f1530ab:cd3bd27c rd.md.uuid=d085c9c0:4d36caf6:ec19a007:ad1e1c74 vconsole.font=latarcyrheb-sun16 $([ -x /usr/sbin/rhcrashkernel-param ] && /usr/sbin/rhcrashkernel-param || :) rhgb quiet
net.ifnames=0 biosdevname=0"
GRUB_DISABLE_RECOVERY="true"

または、/etc/default/grub を変更しておいて「grub2-mkconfig -o /boot/grub2/grub.cfg」を実行しても同じこと。

Fedora のインストール時には /etc/sysconfig/network-scripts/ifcfg-em* と言うシェルスクリプトのインクルードファイルが各々のネットワークインタフェース用に作成される。これがないと ifup とか ifdown が動かない。ifcfg-em* を ifcfg-eth* にリネームして、内容の「NAME=emY」を「NAME=ethX」に変更する。

後はリブートしてやれば、下のように目出度く馴染みの「ethX」 になっているはず。

[root@mill3 ~]# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet6 fe80::6670:2ff:fe11:29eb  prefixlen 64  scopeid 0x20<link>
        ether 64:70:02:11:29:eb  txqueuelen 1000  (Ethernet)
        RX packets 20  bytes 3596 (3.5 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 8  bytes 648 (648.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

ネットワークインタフェースが一つだけならこれでお終いでもよいが、我が家のファイアウォール・ゲートウェイのように複数のインタフェースがある場合は、 それぞれのインタフェースに決定的な名前を付けたい。そのためには udev のルールファイルを使う。

最近のLinuxのディストリビューションでは、/usr/lib にディストリビューションのデフォルト設定ファイルを置き、ローカルの設定変更は /etcの下の同名のファイルでオーバライドするようになっている。Fedora 20 のネットワークインタフェースの udev の場合は、既にデフォルトファイルとして /usr/lib/udev/rules.d/60-net.rules があるので、以下のような /etc/udev/rules.d/60-net.rules を作ってオーバライドする。

[root@mill3 ~]# cat /etc/udev/rules.d/60-net.rules
# PCI device 0x1011:0x0019 (tulip)
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="00:c0:f0:4c:f5:78", ATTR{dev_id}=="0x0", ATTR{type}=="1", KERNEL=="eth*", NAME="eth1"

# PCI device 0x10ec:0x8168 (r8169)
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="60:a4:4c:b5:26:48", ATTR{dev_id}=="0x0", ATTR{type}=="1", KERNEL=="eth*", NAME="eth0"

と言うのは、udev のマニュアルページに書かれていることだが、 実際は違うようだ。この方法を実践してみたが、どうも eth0eth1 の割り付けが不安定で、ブートの度にひっくり返ることがある。そこで、/etc/udev/rules.d の方を 61-net.rules に改名したら安定したように見える。マニュアルページの記載とは異なり、/etc/usr/lib 間の優先度の違いはないように見える。優先したい方(/etc 側)を文字列的に後になるような名前にした方が無難のようだ。

一部のサーバ群の管理では「emY」の方が論理的・仮想的で便利だという意見もあり、それがこの「ethX」から「emY」への変更の理由なのだろうが、過去との互換性の面では困った「新しもの」だ。エイリアスを許せるようにだとかできないのだろうか?

2014年5月8日

BTRFSのFIEMAP/filefragは嘘をつく

FIEMAPioctlとそれを使ったfilefragコマンドは、ディスク上に散らばったファイルの実体(エクステント)を返す・表示するので、ファイルの断片化状態を見たりするのに使う。ところが、BTRFSのFIEMAPで返されるエクステントのディスク上のセクタ番号(=アドレス)は実際の物理ディスク上の位置と一致しない。

これは、BTRFSが内部にRAID機能を持っており、FIEMAPで返されるエクステントの位置情報は、この内部RAID上の位置だからだ。

3.0カーネルに含まれるBTRFSは
  • ストライピング(RAID0)
  • ミラリング(RAID1)
  • ストライピング+ミラリング(RAID10)
  • 複数ディスクの連結(JBOD)
をサポートしており、mkfs.btrfs でファイルシステム構築時にRAIDに使用するディスクを指定、または稼働中に btrfs device add/delete で使用するディスクを増減できる。

この内部RAID上のエクステント情報(FIEMAPが返すのと同じ)は内部的には「論理エクステント」、実際のディスク上のエクステント情報は「物理エクステント」と呼ばれており、外部から利用できる明確なレイヤの境界はなく、したがって論理エクステントと物理エクステントを変換するAPIもないようだ。

この論理エクステントと物理エクステントの変換に関わるコードは fs/btrfs/volumes.c にほとんどが含まれている。

さらにややこしいのは、 BTRFSのファイルをstatして得られるデバイス番号は、本物のメジャー:マイナー番号ではなく、BTRFSが内部的に作成するサブボリュームデバイスの仮想的なデバイス番号だと言うこと。

例えば、下の例では /mnt にマウントされた /dev/md91と言う md のRAID上のファイル /mnt/etc/ld.so.cache を見ているが、/dev/md919:91 のデバイス番号にも関わらず、ファイルを stat すると 0:35 が返される。これはカーネル内部では無名(anonymous)デバイス番号と呼ばれ、メジャー番号は常にゼロ、マイナー番号はデバイス登録の要求がある度に1づつ増える(登録取り消しデバイス番号の再利用はない)。この無名デバイス番号と物理デバイスの関係を解決する直接的な手段はない。

root@h-rn312:~# df -h /mnt/etc/ld.so.cache
Filesystem      Size  Used Avail Use% Mounted on
/dev/md91       396M  2.5M  390M   1% /mnt
root@h-rn312:~# ls -l /dev/md91
brw-rw---T 1 root disk 9, 91 May  7 14:37 /dev/md91
root@h-rn312:~# stat /mnt/etc/ld.so.cache
  File: `/mnt/etc/ld.so.cache'
  Size: 30475         Blocks: 64         IO Block: 4096   regular file
Device: 23h/35d    Inode: 1164        Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2014-05-07 12:50:28.426131463 -0700
Modify: 2014-05-07 12:50:28.426131463 -0700
Change: 2014-05-07 12:50:28.426131463 -0700
 Birth: -

root@h-rn312:~#

現在、FIEMAPを拡張してファイルの物理エクステント情報と物理デバイス番号を返せる機能を開発中。

なお、ファイルサイズが小さい場合(上限サイズは不明)は、ファイル本体がメタデータと一緒に格納されるようで、その場合はfilefragはディスクポジションとしてゼロを表示する。

注意:上で「ディスク」「物理デバイス」と書いたが、これはあくまでもBTRFSの下位のレイヤと言う意味で、実際に物理ディスクまたはそのパーティションでなくても、mdのRAIDだとか、LVMのボリュームだとか、loopデバイスだとか、要するにブロックデバイスなら何でもよい。

2014年5月7日

__func__ はマクロではない

GCCに備わっている__func__は、C99規格に適合して、そのスコープの関数名の文字列にコンパイラが自動変換するので、デバッグ用に便利だ。しかし、この文字列への変換はC言語で言うところの「マクロ」ではない。

だから、以下のような使用はコンパイルエラーになる。


size_t some_func(void)
{
        return strlen("<" __func__ ">");
}


むしろ、以下の宣言と等価と考えた方がよい。

size_t some_func(void)
{
        static const char __func__[] = "some_func";
        return strlen("<" __func__ ">");
}

これはGCCのマニュアルに明記されている。

__FILE__とか__LINE__は本物のマクロだから、strlen("<" __FILE__ ">")のような書き方ができる。

ただし、もう一つの注意点は、__FILE__はプリプロセッサがファイルを処理したときのファイルパスだから、#includeで展開されたファイルパスの__FILE__は、例えば /usr/include/myheader.h になるし、Linuxのカーネルのソースファイル中では fs/btrfs/inode.c のようになる。

2014年5月2日

マウントされたファイルはマウント元をアップデートすべし

Fedroraでは、セキュリティ向上のため、named(BIND)は/var/named/chroot/というディレクトリにchrootして実行するようになっている。ところがこのchrootはデフォルトオプションで、普通のルートでも実行できる。/var/named/chroot/の下には、namedの実行ようの最小限のファイルツリーが必要。これらのファイルは当然「本物の」ルートの下のそれと重複するが、これらは別個のファイルではなく、namedのスタートアップスクリプトが(ほとんどはディレクトリごと)/var/named/chroot/の下のサブディレクトリにBINDのマウントをしている。ハードリンクを作るより明示的で間違いない。

例えば、/var/named//var/named/chroot/var/named/にマウントされるので、namedが/var/named/chroot/にchrootしてもしなくても同一の/var/named/が見える。

ところで我が家のメインDNSサーバは、スパムメイル受信低減とセキュリティ対策のためにdnsblというスクリプトをcronで定期的に起動し、外部のIPアドレスブラックリストを走査し、/var/named/named.dnsblというゾーンファイルを作成してnamedに再ロードさせている。ところが、どういうわけか、このスクリプトがゾーンファイルを/var/named/chroot/var/named/named.dnsblに作成するようになっていた。

定常状態では/var/named/named.dnsblでも/var/named/chroot/var/named/named.dnsblでも同じことだが、システムの起動と終了時にはレイス状態が発生し、/var/named/chroot/var/named/named.dnsblが/var/named/named.dnsblのマウントではなく本物のファイルが作成されてしまうことがある。namedのスタートアップスクリプトはマウント先のディレクトリ(この場合は/var/named/chroot/var/named/)が空であることを確認しており、もし余計なファイルがあるとスタートアップスクリプトがエラー終了し、namedが動作しない。namedが動作しないとあとに続くいろいろなサービスが正常動作せず、結局スタートアップそのものが滞ってしまいシステムが起動できなくなることがあった。

スタートアップのログを見て問題はnamedにあるとの大よその見当はついたので、FedroraのインストールDVDをレスキューモードで起動してHDD上の/var/named/chroot/var/named/named.dnsblを削除してシステムを再起動したらあっさり動いた。持ちおrンスクリプトを変更してマウント先ではなくマウント元を更新するようにした。

と言うと簡単に片付いたように聞こえるが、実は先代のサーバハードウェアから引き続き使っていたPATAのDVDドライブがDVDを読み込めず、山ほどある在庫から持ってきたもう1台のDVDドライブも動作せず、3台目のドライブにして漸く正常動作をしたという逸話付きだ。いやあ大変な数時間だった。