ttt

getttyent

(FreeBSD) panic get_pv_entry: increase vm.pmap.shpgperproc

2009-07-29 23:59:00 | デジタル・インターネット

いろいろと忙しく仕事している、(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じゃん? と思うわけで・・・