zoukankan      html  css  js  c++  java
  • /dev/pmem0p1

    https://qiita.com/YasunoriGoto1/items/6b73ca4bb5e8bd8819ef

    2017年は私にとって忙しくも充実した年となりました。今年はLinuxの不揮発メモリ開発のメンテナーの方とお会いしたり、弊社のOSS開発者を育てるためにOSS Gate弊社社内で行って記事を書いたり、色々なことをやった気がします。

    さて、今年のFujitsu Advent Calender part2 24日目のの記事として、昨年Linux Advent Calenderに記載した不揮発メモリの続編を書きたいと思います。昨年の記事をまだ参照されてない方は、そちらを先に見たほうが良いでしょう。

    本記事ではpart1としてまず不揮発メモリ(NVDIMM)の設定や使い方を紹介したいと思います。2017年のACPIやコミュニティでの動向もこの記事に一緒に記載したかったのですが、長くなるのでpart2として別の記事に記載します。

    お断り(お約束)

    • 本記事の内容は私個人の見解であり、所属する組織として意見を代表したり保証したりするものではありません。
    • 内容については誤りを含む可能性もありますのでご注意ください。(誤りについてご指摘していただけると助かります。)

    NVDIMMのアクセス方法

    NVDIMMのLinuxでのアクセス方法について、改めて使い方について記載します。昨年は「DAX」や「メモリアクセス(?)」と記載していましたが、それから情報更新したものが後ろ2つです。

    1. 通常のblock device/ファイルシステムとして使う
      NVDIMMを従来のストレージと同じように扱うことができます。メモリというよりは単なる高速なストレージとして扱う方法です。この方法であれば、従来のアプリもそのまま不揮発メモリ上で動作させることができます。しかし、HDDなどこれまでの遅いストレージを想定した設計をそのままNVDIMMにも引きずるので、NVDIMM本来の性能は発揮できません。
    2. File System DAX(Direct Access Mode)
      昨年は単にDAXと書いていました。File System 経由で不揮発メモリにアクセスする方法ですが、ファイルをread()/write()する時には不揮発メモリでは無駄となるであろうpage cacheをスキップするほか、その中のファイルをmmap()したときには、当該のNVDIMMのblockを直接アプリに割りあててくれるので、本当に不揮発メモリとしてアクセスできます。従来のファイルシステムのシステムコールも使えるので、比較的使いやすいといえるでしょう。/dev/pmemX.Xとなっているデバイスに対して、XFSまたはext4のファイルシステムを構築し以下のようにmount時にオプションで-o daxと指定することでDAX Filesystemが使えるようになります。
    3. Device DAX
      昨年は「メモリインタフェース(?)」と書いていたアクセス方法です。 使い方はシンプルで、/dev/daxX.Xというデバイスをopen()して、mmap()することで使います。 最速のアクセス方法ですが、一方でopen(), mmap(), close()以外のシステムコールは使えません。また、/dev/daxの領域の管理も壊れた時の復旧も、互換性などもすべてがMWやアプリケーション責任です。kernel/driver側では何もしません。(PMDK(旧名NVML)のライブラリがある程度やってくれるかもしれませんが、私個人はまだちゃんと評価できていません。)

    私見ですが、3つのアクセス方法を表にしてまとめると、このような感じになります。

     通常のblock deviceアクセスFile System DAXDevice DAX
    性能 ×: 無駄が多いため遅い △~○: 従来アクセスでもpage cacheをスキップするなど高速性があがる。本当に不揮発メモリとして直接アクセスする方法もある ◎: 最速
    アプリから見た従来との互換性 あり Filesystem DAX向けにソフトの書き換えが必要 互換性は全くない
    使いやすいさ 従来のストレージと同等 領域の管理はファイルシステムがやってくれるので、Device DAXよりは楽 面倒。領域内の管理はカーネル・ドライバは一切関知しないので、アプリ側で何とかする必要がある。(あるいはPMDKを使う)
    対応ファイルシステム すべて xfs, ext4 なし
    CPU cacheから媒体へのデータ反映 sync()/fsync()/msync()など 今のところ、原則左に同じ(理由はpart2のほうに別途記述)。将来的にはDevice DAXと同じような反映方法が期待されている cpuのcache flush命令(clflush, clflushopt,or clwb)とメモリバリア(sfence)で行う

    Linuxでの不揮発メモリの使い方

    まずは、使ってみるのが一番わかりやすいと思います。このため、kernelが持っているNVDIMMのエミュレーション機能を使って、それぞれどんな感じになるのか試してみましょう。
    環境はFedora27です。

    エミュレーションの設定

    kernelのboot optionで、memmap=XX!YY と指定すると、物理アドレスYYのメモリを先頭にXXバイトのRAMをNVDIMMとしてエミュレーションすることができます。この環境のメモリは12Gなので、RAMとNVDIMMを半分ずつの6Gで割り当てます。

     
    
    # cat /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_TERMINAL="console serial"
    GRUB_CMDLINE_LINUX="console=tty0 console=ttyS0,115200 memmap=6G!6G"
                                                          ^^^^^^^^^^^^
                                                          追加
    GRUB_SERIAL_COMMAND="serial --speed=115200 --unit=0 --word=8 --parity=no --stop==
    1"
    GRUB_DISABLE_RECOVERY="true"
    
    # grub2-mkconfig -o /boot/grub2/grub.cfg
    

    これで再起動すると、dmesgの出力から本来はRAMの領域が不揮発メモリとして扱われていることが分かります。
    BIOS-e820の行は、ファームから与えられた本来のメモリマップ、userとなっている行は、memmap=6G!6Gのboot optionの指定によって変更された後のマッピングです。最後のほうにpersistent (type 12)と出力されています。

     
    [    0.000000] e820: BIOS-provided physical RAM map:
    [    0.000000] BIOS-e820: [mem 0x0000000000000000-0x000000000009fbff] usable
    [    0.000000] BIOS-e820: [mem 0x000000000009fc00-0x000000000009ffff] reserved
    [    0.000000] BIOS-e820: [mem 0x00000000000f0000-0x00000000000fffff] reserved
    [    0.000000] BIOS-e820: [mem 0x0000000000100000-0x00000000bffdefff] usable
    [    0.000000] BIOS-e820: [mem 0x00000000bffdf000-0x00000000bfffffff] reserved
    [    0.000000] BIOS-e820: [mem 0x00000000feffc000-0x00000000feffffff] reserved
    [    0.000000] BIOS-e820: [mem 0x00000000fffc0000-0x00000000ffffffff] reserved
    [    0.000000] BIOS-e820: [mem 0x0000000100000000-0x000000033fffffff] usable  <--- もともとは全部普通のRAMの領域
    [    0.000000] NX (Execute Disable) protection: active
    [    0.000000] e820: user-defined physical RAM map:
    [    0.000000] user: [mem 0x0000000000000000-0x000000000009fbff] usable
    [    0.000000] user: [mem 0x000000000009fc00-0x000000000009ffff] reserved
    [    0.000000] user: [mem 0x00000000000f0000-0x00000000000fffff] reserved
    [    0.000000] user: [mem 0x0000000000100000-0x00000000bffdefff] usable
    [    0.000000] user: [mem 0x00000000bffdf000-0x00000000bfffffff] reserved
    [    0.000000] user: [mem 0x00000000feffc000-0x00000000feffffff] reserved
    [    0.000000] user: [mem 0x00000000fffc0000-0x00000000ffffffff] reserved
    [    0.000000] user: [mem 0x0000000100000000-0x000000017fffffff] usable
    [    0.000000] user: [mem 0x0000000180000000-0x00000002ffffffff] persistent (type 12) <--------ここが不揮発メモリと扱われるようになった領域です。
    [    0.000000] user: [mem 0x0000000300000000-0x000000033fffffff] usable
    
    

    管理コマンド(ndctl)のインストール

    Fedora27では、ndctlコマンドもdnfコマンドで簡単にインストールできます。では、インストールしてみましょう。

     
    
    $ sudo dnf install ndctl
    
    $ sudo ndctl --version
    58.4
    

    さて、NVDIMMのnamespaceのリストを表示させてみます。ndctlの出力はJSON形式で出力されます。ここでは、namespace0.0という領域ができていることが分かります。
    以降は実験として、この領域のnamespaceの設定を変更していきます。

     
    
    $ sudo ndctl list
    {
      "dev":"namespace0.0",
      "mode":"memory",
      "size":6442450944,
      "blockdev":"pmem0",
      "numa_node":0
    }
    

    高速なストレージとして使ってみる

    まずはNVDIMMを高速なストレージとして使ってみましょう。namespaceの設定はndctl create-namespaceコマンドで行いますが、この時のオプションに-m sectorと指定すると、当該namespaceは従来のストレージと同じようにブロック単位でデータを更新する領域になります。またこの際、-lオプションでブロックサイズも指定する必要があります。この値は512byteとか4096byteとかで良いはずですが、今回は4096byteにしています。

     
    
    $ sudo ndctl create-namespace -e "namespace0.0" -m sector -l 4096 -f
    {
      "dev":"namespace0.0",
      "mode":"sector",
      "size":"5.99 GiB (6.44 GB)",
      "uuid":"202373f0-1b85-429a-8c16-6615dd28cb50",
      "sector_size":4096,
      "blockdev":"pmem0s",
      "numa_node":0
    }
    

    これによって/dev/pmem0sというデバイスができるので、後は普通にパーティションの設定やファイルシステムのフォーマットをするだけです。今回はxfsを使ってみましょう。mountも今までのストレージと同じです。

     
    
    $ ls /dev/pmem*
    /dev/pmem0s
    
    $ sudo parted /dev/pmem0s
    (parted) mkpart
    パーティションの名前?  []? nvdimm
    ファイルシステムの種類?  [ext2]? xfs
    開始? 1M
    終了? 6G
    
    $ ls /dev/pmem*
    /dev/pmem0s  /dev/pmem0s1
    
    
    $ sudo mkfs.xfs /dev/pmem0s1 -f
    meta-data=/dev/pmem0s1           isize=512    agcount=4, agsize=392640 blks
             =                       sectsz=4096  attr=2, projid32bit=1
             =                       crc=1        finobt=1, sparse=0, rmapbt=0, reflink=0
    data     =                       bsize=4096   blocks=1570560, imaxpct=25
             =                       sunit=0      swidth=0 blks
    naming   =version 2              bsize=4096   ascii-ci=0 ftype=1
    log      =internal log           bsize=4096   blocks=2560, version=2
             =                       sectsz=4096  sunit=1 blks, lazy-count=1
    realtime =none                   extsz=4096   blocks=0, rtextents=0
    
    $ sudo mkdir /mnt/pmem
    
    $ sudo mount /dev/pmem0s1 /mnt/pmem
    

    これで、NVDIMMは/mnt/pmem上の普通のxfsファイルシステムとして使うことができるようになります。

    Filesystem DAXを使う(mmap編)

    では、こんどはFilesystem DAXを使ってみましょう。Filesystem DAXとして使うためには、まず先ほどのnamespaceの設定を変更する必要があります。先ほどの/mnt/pmemをumountした後、ndctlのcreate-namespaceコマンドのオプション-m memoryを使って再設定しましょう。今度はbyte単位でアクセスできるモードですから、ブロックサイズを指定する必要はありません。

     
    
    $ sudo ndctl create-namespace -e "namespace0.0" -m memory -f
    {
      "dev":"namespace0.0",
      "mode":"memory",
      "size":"5.90 GiB (6.34 GB)",
      "uuid":"dc47d0d7-7d8f-473e-9db4-1c2e473dbc8f",
      "blockdev":"pmem0",
      "numa_node":0
    }
    

    今度は/dev/pmem0というデバイス名になります。先ほどは/dev/pmem0sでしたからsがあるか無いかの違いで見分けることができますね。パーティションやフォーマットは通常のストレージと同じですが、Filesystem DAXを有効にするためにはmountのオプションで-o daxをつける必要があります。

     
    
    $ sudo parted /dev/pmem0
    
    GNU Parted 3.2
    /dev/pmem0 を使用
    GNU Parted へようこそ! コマンド一覧を見るには 'help' と入力してください。
    (parted) mklabel gpt
    (parted) mkpart
    パーティションの名前?  []? nvdimm
    ファイルシステムの種類?  [ext2]? xfs
    開始? 1M
    終了? 6G
    
    $ ls /dev/pmem*
    /dev/pmem0  /dev/pmem0p1
    
    $ sudo mkfs.xfs /dev/pmem0p1
    meta-data=/dev/pmem0p1           isize=512    agcount=4, agsize=386816 blks
             =                       sectsz=4096  attr=2, projid32bit=1
             =                       crc=1        finobt=1, sparse=0, rmapbt=0, reflink=0
    data     =                       bsize=4096   blocks=1547264, imaxpct=25
             =                       sunit=0      swidth=0 blks
    naming   =version 2              bsize=4096   ascii-ci=0 ftype=1
    log      =internal log           bsize=4096   blocks=2560, version=2
             =                       sectsz=4096  sunit=1 blks, lazy-count=1
    realtime =none                   extsz=4096   blocks=0, rtextents=0
    
    $ sudo mount -o dax /dev/pmem0p1 /mnt/pmem
                  ^^^^^
                  Filesystem DAXの指定
    

    なお、Filesystem DAXはまだ"Experimental"、つまりまだ実験的な未完成の機能として扱われているので、mountするとsyslogに警告が出ます。

     
    Dec 23 12:29:20 localhost kernel: XFS (pmem0p1): DAX enabled. Warning: EXPERIMENTAL, use at your own risk
    Dec 23 12:29:20 localhost kernel: XFS (pmem0p1): Mounting V5 Filesystem
    Dec 23 12:29:20 localhost kernel: XFS (pmem0p1): Ending clean mount
    

    さて、FilesystemDAXでは、NVDIMM内の領域のファイルをmmap()すると、不揮発メモリの領域が直接割り当てられます。それが本当かどうか実験してみましょう。

    以下のような、簡単なプログラムを用意します。

     
    #include <stdio.h>
    #include <sys/mman.h>
    
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <string.h>
    
    #define MMAP_SIZE ((size_t)1024*1024*1024)
    
    int main(int argc, char *argv[])
    {
            int fd;
            void *p;
    
            if (argc != 2) {
                    printf("dax_read_loop <file or device name>
    ");
                    return -1;
            }
    
            fd = open(argv[1], O_RDWR);
            if (fd == -1) {
                    perror("open");
                    return -1;
            }
    
            p = mmap(NULL, MMAP_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
            if (p == MAP_FAILED) {
                    perror("mmap");
                    return -1;
            }
    
            printf("mmapped p=%p
    ", p);
    
            memcpy(p, "hogehoge
    ", sizeof("hogehoge
    "));
            msync(p, 4096, MS_SYNC);
            for(;;);
    
    }
    

    コンパイルして実行します。プロセスに割り当てられた仮想アドレスが表示されます。

     
    
    $ cc dax_read_loop.c -o dax_read_loop
    $ sudo touch /mnt/pmem/hogehoge
    $ sudo ./dax_read_loop /mnt/pmem/hogehoge
    mmapped p=0x7fd0df600000
    

    上記仮想アドレスに対して、対応する物理アドレスを知るにはcrashコマンドを使ってkernelの中を覗く方法を使ってみます。
    注)crashでこのようなlive dumpの機能を使うには、あらかじめ以下のような準備が必要なのですが、本題から外れてくるので手順は省略します。興味のある人は別途調べてみてください。

    • kernel の debuginfoをダウンロード・インストール
    • 上記に対応するkernelにupdate(debuginfoは最新版のkernelにしか用意されません)
    • crashが古くてlive dumpを開くことができない場合は、最新のcrashをビルド

    では、見てみましょう。

     
    
    $ sudo ./crash /usr/lib/debug/lib/modules/4.14.7-300.fc27.x86_64/vmlinux /proc/kcore
    
    crash 7.2.0
    Copyright (C) 2002-2017  Red Hat, Inc.
     :
      :
    
    crash> ps |grep dax <----実行したプロセスを探す
    > 25956    948   4  ffff9b5b6511ddc0  RU   0.0 1128512   1360  dax_read_loop
    
    
    crash> set 25956 <----ターゲットをこのプロセスに設定
        PID: 25956
    COMMAND: "dax_read_loop"
       TASK: ffff9b5b6511ddc0  [THREAD_INFO: ffff9b5b6511ddc0]
        CPU: 4
      STATE: TASK_RUNNING (ACTIVE)
    
    crash> vtop 0x7fd0df600000 <----プロセスが表示した仮想アドレスから物理アドレスを求める
    VIRTUAL     PHYSICAL
    7fd0df600000  186315000 <----割り当てられた物理アドレス
    
       PML: 12e3247f8 => 13f363067
       PUD: 13f363a18 => 12aa66067
       PMD: 12aa667d8 => 12b264067
       PTE: 12b264000 => 8400000186315a25
      PAGE: 186315000
    
          PTE         PHYSICAL   FLAGS
    8400000186315a25  186315000  (PRESENT|USER|ACCESSED|NX)
    
          VMA           START       END     FLAGS FILE
    ffff9b5b6529fed8 7fd0df600000 7fd123fb4000 380000fb /root/mnt/pmem/hogehoge <---開いているファイル
    
          PAGE        PHYSICAL      MAPPING       INDEX CNT FLAGS
    ffffe70a4618c540 186315000                0        0  0 0
    
    

    先ほどdmesgで表示されたアドレスと比べると、先ほどの物理メモリが不揮発メモリの領域の範囲内であることが分かります。
    このように、Filesystem DAX上のファイルをmmap()すると、不揮発メモリが直接割り当てられるようになるのです。

     
    [    0.000000] user: [mem 0x0000000180000000-0x00000002ffffffff] persistent (type 12) 
                                       ^^^^^^^^^          ^^^^^^^^^
    

    Filesystem DAXを使う(read()/write()編)

    Filesystem DAXでは通常のread()/write()システムコールも普通に使うことができます。この場合、page cacheすなわちファイルのキャッシュをスキップするようになります。これを確認してみましょう。

    通常のファイルシステムでは、システムにFreeMemoryがまだいっぱい残っている状態の時にtarのアーカイブを展開すると、カーネルはそれらのファイルを一旦page cacheに残そうとします。これは /proc/meminfo の Inactive(file) の値が増えるかどうかで確認することができます。大きなtarballほど効果が分かりやすいので、kernelのソースを使ってまずは通常のファイルシステム上に展開してみましょう。

     
    
    $ cat /proc/meminfo
    MemTotal:        6109588 kB
    MemFree:         5202244 kB  <---空きメモリが大量にある
    MemAvailable:    5699356 kB
    Buffers:            4620 kB
    Cached:           684080 kB
    SwapCached:            0 kB
    Active:           502456 kB
    Inactive:         254672 kB
    Active(anon):      69056 kB
    Inactive(anon):      304 kB
    Active(file):     433400 kB
    Inactive(file):   254368 kB <--- tarを展開する前はこれぐらい
    
      :
      :
    
    $ sudo tar xvf /home/goto/linux-4.14.tar.gz
    
    $ cat /proc/meminfo
    MemTotal:        6109588 kB
    MemFree:         4048288 kB  <---空きメモリが使われた
    MemAvailable:    5504288 kB
    Buffers:            4620 kB
    Cached:          1585188 kB
    SwapCached:            0 kB
    Active:           506596 kB
    Inactive:        1152844 kB
    Active(anon):      70292 kB
    Inactive(anon):      308 kB
    Active(file):     436304 kB
    Inactive(file):  1152536 kB  <--- tarを展開するとpage cacheが使われるので、この値が増える
      :
       :
    

    このtarを展開したディレクトリを削除すると、page cacheはもう必要なくなるため、Inactive(file)の値が減ることになります。

     
    
    $ sudo rm -rf linux-4.14/
    
    MemTotal:        6109588 kB
    MemFree:         5189832 kB <---増えた
    MemAvailable:    5693512 kB
    Buffers:            4620 kB
    Cached:           689852 kB
    SwapCached:            0 kB
    Active:           508196 kB
    Inactive:         255280 kB
    Active(anon):      69548 kB
    Inactive(anon):      308 kB
    Active(file):     438648 kB
    Inactive(file):   254972 kB <---page cacheが不要になったので、MemFreeに返却された
    

    では、Filesystem DAX上で展開するとどうなるでしょうか?比較してみましょう。

     
    
    MemTotal:        6109588 kB
    MemFree:         5177504 kB
    MemAvailable:    5694032 kB
    Buffers:            4620 kB
    Cached:           702828 kB
    SwapCached:            0 kB
    Active:           522180 kB
    Inactive:         254088 kB
    Active(anon):      69456 kB
    Inactive(anon):      304 kB
    Active(file):     452724 kB
    Inactive(file):   253784 kB <-----もとの量
      :
      :
    
    $ cd /mnt/pmem
    $ sudo tar xvf ~goto/linux-4.14.tar.gz
    
    MemTotal:        6109588 kB
    MemFree:         4971340 kB
    MemAvailable:    5526536 kB
    Buffers:            4620 kB
    Cached:           704212 kB
    SwapCached:            0 kB
    Active:           523636 kB
    Inactive:         254116 kB
    Active(anon):      69604 kB
    Inactive(anon):      304 kB
    Active(file):     454032 kB
    Inactive(file):   253812 kB  <---ほとんど増えてない
      :
      :
    

    先ほどと比べてほとんど増えていません。このように、Filesystem DAXには、page cacheを使わずにデータをread()/write()するというメリットもあります。

    Device DAXとして使う

    では、最後にDevice DAXとして使ってみましょう。/mnt/pmem を umount したら、もう一度ndctl create-namespaceコマンドを使って、今度はDevice DAXモードにしてみましょう。

     
    
    $ sudo ndctl create-namespace -e "namespace0.0" -m dax -f
    {
      "dev":"namespace0.0",
      "mode":"dax",
      "size":"5.90 GiB (6.34 GB)",
      "uuid":"1d516f0a-b2c7-49e8-a90a-ee9551ef6d5e",
      "daxregion":{
        "id":0,
        "size":"5.90 GiB (6.34 GB)",
        "align":2097152,
        "devices":[
          {
            "chardev":"dax0.0",
            "size":"5.90 GiB (6.34 GB)"
          }
        ]
      },
      "numa_node":0
    }
    

    このモードでは、/dev/daxというデバイスができます。

     
    
    $ ls /dev/dax*
    /dev/dax0.0
    

    使い方は先ほどのmmap()のプログラムを使って/dev/dax0.0を開くだけです。(ただし、msync()はエラーで戻ってくるので注意が必要です。)

     
    
    $ sudo ./dax_read_loop /dev/dax0.0
    mmapped p=0x7fc4aa200000
    

    このデバイスに対してできることは、open(), close(), mmap()だけです。
    /dev/dax0.0の領域の管理は完全にアプリレイヤに任されているので、既存のソフトウェアからDevice DAXを使うというのは無理そうです。PMDKのライブラリを使ったほうが良さそうな予感がしますね。

    まとめ

    ざっとNVDIMMの3種類の使い方を紹介しました。Fedora27のカーネルがNVDIMMのエミュレーションをデフォルトで対応するようになったので、自分でカーネルのコンパイルオプションを指定してビルドする必要もなくなり、昨年よりずいぶん楽に、そして身近になってきています。ぜひ試してみてください。

    では、part2に続きます。

  • 相关阅读:
    PHP抓取页面的几种方式
    MySQL性能优化的最佳20+条经验
    linux下导入、导出mysql数据库命令
    8个必备的PHP功能开发
    jquery中的children()和contents()的区别
    centos 通用开发工具及库安装 有了它不用愁了
    将bat批处理文件注册成windows服务
    squid隐藏squid的版本号
    squid如何屏蔽User-Agent为空的请求
    RHEL/CentOS 6.x使用EPEL6与remi的yum源安装MySQL 5.5.x
  • 原文地址:https://www.cnblogs.com/dream397/p/13805136.html
Copyright © 2011-2022 走看看