いろいろと忙しく仕事している、(5月ころbuildworldした)FreeBSD 7.2-STABLEなマシンで、先日、kernel panicが発生しました。
get_pv_entry: increase vm.pmap.shpgperproc
というメッセージが残されていました。
しかも、このkernel panic、立て続けに、数回発生。
けっこう安定して動作していたのに、突然の連続kernel panicで、ちょっと嫌な気分。
ネット検索してみると、この辺に、似たような話を発見。7.0-RELEASE-p5の場合で、ちょっと違いもありますが。
kernel: Approaching the limit on PV entries...
http://docs.freebsd.org/cgi/getmsg.cgi?fetch=2052953+0+archive/2008/freebsd-questions/20081012.freebsd-questions
kernel panicするという話も、こちらに出てます。
http://docs.freebsd.org/cgi/getmsg.cgi?fetch=2082943+0+archive/2008/freebsd-questions/20081012.freebsd-questions
pv_entryについて、少し解説がされてました。
http://docs.freebsd.org/cgi/getmsg.cgi?fetch=2094590+0+archive/2008/freebsd-questions/20081012.freebsd-questions
☆
このpanicを発生させているのは、src/sys/i386/i386/pmap.cです。
7.2-STABLEは、RELENG_7ブランチなので、ソースコードはこれ(i386版なので)。
http://www.freebsd.org/cgi/cvsweb.cgi/src/sys/i386/i386/pmap.c?rev=1.594.2.18;content-type=text%2Fplain
get_pv_entryという関数の中ですね。
私が見たケースでは、
Approaching the limit on PV entries, consider increasing either the vm.pmap.shpgperproc or the vm.pmap.pv_entry_max tunable.
というメッセージは残っていませんでした。ディスプレイを接続していなのでわかりませんが、コンソール画面には出ていたかもしれないですけど。
さてさて、
get_pv_entry: increase vm.pmap.shpgperproc
でkernel panicしてるのは、ここですが・・・
/*
* Access to the ptelist "pv_vafree" is synchronized by the page
* queues lock. If "pv_vafree" is currently non-empty, it will
* remain non-empty until pmap_ptelist_alloc() completes.
*/
if (pv_vafree == 0 || (m = vm_page_alloc(NULL, colour, (pq ==
&vm_page_queues[PQ_ACTIVE] ? VM_ALLOC_SYSTEM : VM_ALLOC_NORMAL) |
VM_ALLOC_NOOBJ | VM_ALLOC_WIRED)) == NULL) {
if (try) {
pv_entry_count--;
PV_STAT(pc_chunk_tryfail++);
return (NULL);
}
/*
* Reclaim pv entries: At first, destroy mappings to
* inactive pages. After that, if a pv chunk entry
* is still needed, destroy mappings to active pages.
*/
if (pq == NULL) {
PV_STAT(pmap_collect_inactive++);
pq = &vm_page_queues[PQ_INACTIVE];
} else if (pq == &vm_page_queues[PQ_INACTIVE]) {
PV_STAT(pmap_collect_active++);
pq = &vm_page_queues[PQ_ACTIVE];
} else
panic("get_pv_entry: increase vm.pmap.shpgperproc");
pmap_collect(pmap, pq);
goto retry;
}
よくわからないっす。
panicするんじゃなくて、システムコール(?)を失敗させるとか、もしくは、processをkillしちゃうとか(どうも、ここで必要なメモリ量は、process数と関係しているっぽいので)、何かもう少しましな方法があるんじゃないかと思うのですが・・・
swap不足になったとき、processがどんどこkillされたりしますねよ。あんな感じでいいので、とりあえず、OSとしては運用を継続して欲しいところです。
panicされちゃうと、file systemがdirtyのままになるので、fsckに時間がかかってたまらないし、なによりも、ファイル内容の破壊が怖いです。
☆
とりあえずの解決方法。
vm.pmap.shpgperprocを増やせ、といってるので、増やしてみました。
これ、OSが立ち上がった後では変更できないパラメータなので、/boot/loader.confで指定しなければなりません。
ですが・・・、どんだけ増やせばいいのか? よくわかんないです。
デフォルトが200で、とりあえず50増やしてみて、やっぱりpanic。また50増やして・・・とやっていって、400でどうやらpanicしなくなったようです。
何をしたとき、pv_entryが不足するのか?・・・それが気になって、こんなシェルスクリプトを走らせておいて、いろいろ負荷をかけて観察してみました。
#! /bin/sh
( while [ /bin/true ] ; do
sysctl vm.pmap
sleep 0.5
done ) | awk '
/pg_ps_enabled/ { printf("\n"); }
{ printf("%d ", $2); }
'
実行すると、こんな感じで、sysctl vm.pmapで表示される値を、表示しつづける、というもの。一部のパラメータは(ひたすらカウントアップしていくらしい)、signed intのために、負の値へ突入しちゃってますね。
0 0 0 68464 -2143457166 -2144110094 0 8838034 8840181 2147 652928 0 0 0 0 400 3362352
0 0 0 68797 -2143455008 -2144107939 0 8838043 8840191 2148 652931 0 0 0 0 400 3362352
0 0 0 68922 -2143449222 -2144106732 0 8838049 8840211 2162 657510 0 0 0 0 400 3362352
ところで、このスクリプト、改行位置が1列分、ずれてるみたい。awkで実行する2行を入れ替えればいいのかな。
で、観察してみたんですが、panicしなくなっちゃって、結局よくわからなかったです。
けっこうたくさんのプロセスが起動していても、手計算してみると、80%くらい、余裕で空きがあるように見えたんですが。
よくわかんないですねぇ・・・
% sysctl vm.pmap
vm.pmap.pmap_collect_active: 0
vm.pmap.pmap_collect_inactive: 0
vm.pmap.pv_entry_spare: 68508
vm.pmap.pv_entry_allocs: -2142835831
vm.pmap.pv_entry_frees: -2143492075
vm.pmap.pc_chunk_tryfail: 0
vm.pmap.pc_chunk_frees: 8840296
vm.pmap.pc_chunk_allocs: 8842453
vm.pmap.pc_chunk_count: 2157
vm.pmap.pv_entry_count: 656244
vm.pmap.pde.promotions: 0
vm.pmap.pde.p_failures: 0
vm.pmap.pde.mappings: 0
vm.pmap.pde.demotions: 0
vm.pmap.shpgperproc: 400
vm.pmap.pv_entry_max: 3362352
vm.pmap.pg_ps_enabled: 0
しかも、これって、一般ユーザー権限でkernel panicを発生させられちゃうので、local user exploitableなDoSじゃん? と思うわけで・・・