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月28日
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に共通する問題を抱えている。
HDDのあったところにSSDを装着して蓋を閉め、バッテリを戻してラップトップを起動すると、もしかしたらBIOSが何か言ってくるかもしれないが、すべて肯定的に返事をすれば、きっと立ち上がり時間が1/10になっていることに驚くはず。
元のHDDより大容量のSDDを使ったので、ディスクの後ろの方が余る。小生が実践した換装では、86GBほど余ってしまった。WindowsでCドライブしか使っていなければ、新たなパーティションを作ってDドライブとして使うのが一番簡単。小生の場合はちょっと複雑で、Cの後ろにバックアップの対象としないVMWareの仮想ドライブファイルを格納するVドライブが既にある(バックアップはVMWareのゲストOS内で実行)。新たなパーティションでやたらドライブ数を増やしても仕方がないので、この新しいパーティションはVドライブをコピーしてきて、以前のVドライブの場所はCドライブを延長しようかと考えている。ま、この辺は各自でどうぞ。
これで7年前のラップトップが快調に甦った。あと3年ぐらいは使えそうな感じがする。
Windows Vista(Business、32bit)と言うWindows8ほどではないものの最悪に近いOSであることと画面が1280x800と少々小さめであることを除けば、最近のMacほどはないものの、ウェッジ型の薄く見える形状で気に入っている。毎年のバケーションにもいろいろな国に持って行っており、バッテリーは3代目だ。
気に入っているとは言え、このラップトップは、1年以上使用されているすべてのPCに共通する問題を抱えている。
お・そ・い
今年は家族メンバーのPCのアップグレードが2件予定されており(1件は実施済み)、新しいラップトップに買い換える予算がない。 そこで、最小限の費用での延命策を検討した結果、採用したのが以下の2項目。
ドングルを挿して、付属のドライバをインストールする。コントロールパネルから内臓の 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の容量は内部チップの面積(数)に正比例し、これが価格の大部分を占めるが、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」にしたらあっけなく治った。
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の出力フォーマットの変更(こちらの方が影響の可能性が大きそう)もさることながら、実害の可能性は低いと思うが、もしかしたら自家製のツールのもうとっくに忘れた部分でわけの分からない壊れ方をするかもしれない。
ぐぐってみると、どうもこの仕様はFC15あたりから導入されたらしいが、「emY から ethX に戻したい」と言う同じ悩みを抱えた人は大勢いるらしく、結構な数の記事がヒットする。それらを色々試してみるがうまく行かない。結局たどり着いたのはこちらの記事。
これによると、以下の2ヶ所を手当てすればよい。
まず、カーネルのブート時にemYへのリネームを禁止する引数を渡す。
/boot/grub/grub.conf と言うgrub独自言語のファイルが /boot/grub2/grub.cfg と言うシェルスクリプトに変わってしまったことにも驚いたが、とにかく集めた情報を基に、以下のパラメタ(net.ifnames=0 biosdevname=0)をカーネルの起動時コマンドラインに追加する。
上記記事によれば、FC18までは「biosdevname=0」だけでよかったが、FC19以降ではこれだけだと以下のようなドライバの付けたデバイス名になってしまう。
/boot/grub2/grub.cfg は新しいカーネルがインストールされる度に書き換えられてしまうので、この変更を将来も自動的に行うためには、/etc/default/grub にこれらを追加しておく。
または、/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」 になっているはず。
ネットワークインタフェースが一つだけならこれでお終いでもよいが、我が家のファイアウォール・ゲートウェイのように複数のインタフェースがある場合は、 それぞれのインタフェースに決定的な名前を付けたい。そのためには udev のルールファイルを使う。
最近のLinuxのディストリビューションでは、/usr/lib にディストリビューションのデフォルト設定ファイルを置き、ローカルの設定変更は /etcの下の同名のファイルでオーバライドするようになっている。Fedora 20 のネットワークインタフェースの udev の場合は、既にデフォルトファイルとして /usr/lib/udev/rules.d/60-net.rules があるので、以下のような /etc/udev/rules.d/60-net.rules を作ってオーバライドする。
と言うのは、udev のマニュアルページに書かれていることだが、 実際は違うようだ。この方法を実践してみたが、どうも eth0 と eth1 の割り付けが不安定で、ブートの度にひっくり返ることがある。そこで、/etc/udev/rules.d の方を 61-net.rules に改名したら安定したように見える。マニュアルページの記載とは異なり、/etc と /usr/lib 間の優先度の違いはないように見える。優先したい方(/etc 側)を文字列的に後になるような名前にした方が無難のようだ。
一部のサーバ群の管理では「emY」の方が論理的・仮想的で便利だという意見もあり、それがこの「ethX」から「emY」への変更の理由なのだろうが、過去との互換性の面では困った「新しもの」だ。エイリアスを許せるようにだとかできないのだろうか?
選んだのは現在最新の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 でオーバライド
まず、カーネルのブート時に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 のマニュアルページに書かれていることだが、 実際は違うようだ。この方法を実践してみたが、どうも eth0 と eth1 の割り付けが不安定で、ブートの度にひっくり返ることがある。そこで、/etc/udev/rules.d の方を 61-net.rules に改名したら安定したように見える。マニュアルページの記載とは異なり、/etc と /usr/lib 間の優先度の違いはないように見える。優先したい方(/etc 側)を文字列的に後になるような名前にした方が無難のようだ。
一部のサーバ群の管理では「emY」の方が論理的・仮想的で便利だという意見もあり、それがこの「ethX」から「emY」への変更の理由なのだろうが、過去との互換性の面では困った「新しもの」だ。エイリアスを許せるようにだとかできないのだろうか?
2014年5月8日
BTRFSのFIEMAP/filefragは嘘をつく
FIEMAPのioctlとそれを使ったfilefragコマンドは、ディスク上に散らばったファイルの実体(エクステント)を返す・表示するので、ファイルの断片化状態を見たりするのに使う。ところが、BTRFSのFIEMAPで返されるエクステントのディスク上のセクタ番号(=アドレス)は実際の物理ディスク上の位置と一致しない。
これは、BTRFSが内部にRAID機能を持っており、FIEMAPで返されるエクステントの位置情報は、この内部RAID上の位置だからだ。
3.0カーネルに含まれるBTRFSは
この内部RAID上のエクステント情報(FIEMAPが返すのと同じ)は内部的には「論理エクステント」、実際のディスク上のエクステント情報は「物理エクステント」と呼ばれており、外部から利用できる明確なレイヤの境界はなく、したがって論理エクステントと物理エクステントを変換するAPIもないようだ。
この論理エクステントと物理エクステントの変換に関わるコードは fs/btrfs/volumes.c にほとんどが含まれている。
さらにややこしいのは、 BTRFSのファイルをstatして得られるデバイス番号は、本物のメジャー:マイナー番号ではなく、BTRFSが内部的に作成するサブボリュームデバイスの仮想的なデバイス番号だと言うこと。
例えば、下の例では /mnt にマウントされた /dev/md91と言う md のRAID上のファイル /mnt/etc/ld.so.cache を見ているが、/dev/md91 は 9:91 のデバイス番号にも関わらず、ファイルを stat すると 0:35 が返される。これはカーネル内部では無名(anonymous)デバイス番号と呼ばれ、メジャー番号は常にゼロ、マイナー番号はデバイス登録の要求がある度に1づつ増える(登録取り消しデバイス番号の再利用はない)。この無名デバイス番号と物理デバイスの関係を解決する直接的な手段はない。
現在、FIEMAPを拡張してファイルの物理エクステント情報と物理デバイス番号を返せる機能を開発中。
なお、ファイルサイズが小さい場合(上限サイズは不明)は、ファイル本体がメタデータと一緒に格納されるようで、その場合はfilefragはディスクポジションとしてゼロを表示する。
注意:上で「ディスク」「物理デバイス」と書いたが、これはあくまでもBTRFSの下位のレイヤと言う意味で、実際に物理ディスクまたはそのパーティションでなくても、mdのRAIDだとか、LVMのボリュームだとか、loopデバイスだとか、要するにブロックデバイスなら何でもよい。
これは、BTRFSが内部にRAID機能を持っており、FIEMAPで返されるエクステントの位置情報は、この内部RAID上の位置だからだ。
3.0カーネルに含まれるBTRFSは
- ストライピング(RAID0)
- ミラリング(RAID1)
- ストライピング+ミラリング(RAID10)
- 複数ディスクの連結(JBOD)
この内部RAID上のエクステント情報(FIEMAPが返すのと同じ)は内部的には「論理エクステント」、実際のディスク上のエクステント情報は「物理エクステント」と呼ばれており、外部から利用できる明確なレイヤの境界はなく、したがって論理エクステントと物理エクステントを変換するAPIもないようだ。
この論理エクステントと物理エクステントの変換に関わるコードは fs/btrfs/volumes.c にほとんどが含まれている。
さらにややこしいのは、 BTRFSのファイルをstatして得られるデバイス番号は、本物のメジャー:マイナー番号ではなく、BTRFSが内部的に作成するサブボリュームデバイスの仮想的なデバイス番号だと言うこと。
例えば、下の例では /mnt にマウントされた /dev/md91と言う md のRAID上のファイル /mnt/etc/ld.so.cache を見ているが、/dev/md91 は 9: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言語で言うところの「マクロ」ではない。
だから、以下のような使用はコンパイルエラーになる。
むしろ、以下の宣言と等価と考えた方がよい。
これはGCCのマニュアルに明記されている。
__FILE__とか__LINE__は本物のマクロだから、strlen("<" __FILE__ ">")のような書き方ができる。
ただし、もう一つの注意点は、__FILE__はプリプロセッサがファイルを処理したときのファイルパスだから、#includeで展開されたファイルパスの__FILE__は、例えば /usr/include/myheader.h になるし、Linuxのカーネルのソースファイル中では fs/btrfs/inode.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台目のドライブにして漸く正常動作をしたという逸話付きだ。いやあ大変な数時間だった。
例えば、/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台目のドライブにして漸く正常動作をしたという逸話付きだ。いやあ大変な数時間だった。
登録:
投稿 (Atom)