kubo39's blog

ただの雑記です。

SSDのはなし

ざっくり。基本的にSSDの特性はNANDの特性で、そこをファームのコントローラーがどのくらいサポートするかで製品の質が決まる。

寿命

  • NANDの寿命 - よく言われるのが書き換え回数

    • SLCだと約10万回
    • 書き換え回数 = イレース回数
      • ブロック単位で行われる(Linuxなんかでいうディスクブロックとは別もの)
    • これが多くなるとビットエラーが多くなったり
  • SSD readonlyの場合

    • readにも寿命がある(何百万回という単位だけど)
    • 何度もreadするとまわりのブロックに電荷の影響 -> ビット化けがおきる
    • 産業用だと定期的にリフレッシュかける機能を入れているものもある
  • データリテンション

    • NANDはデータを書き込んでも電源が入っていない状態のまま放置すると時間の経過とともにデータが化ける
    • 書いた後電源入れずにどのくらいもつかはだいたいの製品は出してる -> 約10年(SLC)
  • NANDは大容量化で品質落ちている

    • 大容量だと工夫しなくても平準化する
    • 負荷が特定のブロックに集中しないように
  • NANDは小さいデータに弱い

    • 穴あきになると効率よく使えない -> スペアブロックも穴あきになるから
    • ファイルの追記は大丈夫(ファイルシステムやバッファキャッシュが緩衝してる?)

故障原因

  • データは電源断で壊れる(SSDとして壊れるというのとは違う)
  • リード中壊れることはまずない(基本的にSSDというものはそれでは壊れない)

  • NANDが壊れるとは -> 論理と物理のアドレスマッピングができなくなる

    • 民製品ではSSDとして認識できなくなってしまう
    • 産業用だと壊れる前に戻ってはじめられるものもある
  • ECC訂正

    • エラー訂正
    • これで訂正できないものをアンクエラーとよぶ
    • アンクエラーまででるとそのブロックはもう使えない
    • スペアブロックがなくなれば書けなくなる
  • SSDマザーボードとの相性はある

    • 電源とか
    • クロック系は露骨に相性がわかる(明らかに遅いとか、そもそも認識しないとか)
  • NANDの物理ブロックは観測できない

    • 産業用だとbad blockを検出するツールを提供しているところはある
  • 一番こわいのはファームウェアの故障

    • OS更新時とか一気に書いてるときのの電源オフは避ける

FAQ?

  • どういうSSDがアプリケーションによいか?

    • 想定負荷をかけるしかない
  • 小さい単位でのランダムアクセスをどうやって観測するか?

    • バスアナライザを使うのが確実

静岡旅行

サッカー観戦ついでに一泊二日で静岡に行った。

1日目

勝ったので最高でした。パトリックのゴールシーンではゴール裏みな狂喜乱舞して最高だったし最高だった。

大庄水産です。

2日目

写真とかないけど、駿府城跡と日本平動物園に行った。 日本平動物園レッサーパンダとかジャガーとかゴマフアザラシが間近でみれて最高だった。

まとめ

静岡とにかく飯美味いし街並みもきれいだし最高なのでまた行きたい。

Rustでバイナリなライブラリ

最近いじるときにどっかに情報まとめとくといいかな、と思い。他にもあれば是非教えてください。

dfmtのnightly binaryを作成するレポジトリを作った

レポジトリは これ

作成手順を残しておく。

とりあえずtravis gemの力を借りる。ここで二段階認証を行うのだが、やけに失敗した。 このタイミングでapi_keyが作られている。

$ gem install travis
$ travis setup releases -r kubo39/dfmt-bin

あとはtagつきコミットをpushすれば勝手にビルドしてくれるだろうと思ったのだが、なぜだかやってくれなかったのでbranches: masterの指定にした。

とりあえずdailyでcron設定したけれど、置いといたスクリプトが動くのかはわからない。

binutilsでどういうオプション使うの?というはなし

manみて適切なの使えば?というはなしなんだろうけど、まあとっかかりとかあるし。

objdump

だいたい -Cd(S) もしくは -Cdr(S)をつかってる気がする。Dだと -C dlang とかつける。でもわりとその場その場で変えてるような気もする。

nm

-f はポータブルじゃないので、–format=posix とかにしてる。

hexdump

-C くらい?使い込んでないだけかも。

size

意外と知らない人もいるけど、なかなか便利なやつ。(マイコンとかだとサイズ気になる)

単にサイズ知りたいときは10進数にしてる。

$ arm-none-eabi-size -d examples/itm/itm.bin
   text    data     bss     dec     hex filename
   8716     436      20    9172    23d4 examples/itm/itm.bin

セクション毎でみたいときは、だいたい -Ax にして16進数にしてる。

$ arm-none-eabi-size -Ax examples/itm/itm.bin
examples/itm/itm.bin  :
section             size         addr
.isr_vector        0x188    0x8000000
.text              0x174    0x8000188
.rodata           0x2098    0x80002fc
.bss                0x14   0x20000000
.data                0x8    0x8002394
.got                0x18    0x800239c
.got.plt             0xc    0x80023b4
.debug_str         0x62f          0x0
.debug_loc         0x226          0x0
.debug_abbrev      0x372          0x0
.debug_info        0x9ba          0x0
.debug_ranges       0xc0          0x0
.debug_macinfo       0x3          0x0
.debug_pubnames    0x273          0x0
.debug_pubtypes    0x1b1          0x0
.comment            0x12          0x0
.ARM.attributes     0x45          0x0
.debug_frame       0x1a0          0x0
.debug_line        0x2c4          0x0
Total             0x42f7

readelf

これこそいろいろ使いわけるような。でも -l と -S と -h は使用率高い気する。

あとCortex-Mだと -A オプションとか。

$ arm-none-eabi-readelf -A examples/led/led.bin
Attribute Section: aeabi
File Attributes
  Tag_conformance: "2.09"
  Tag_CPU_name: "cortex-m4"
  Tag_CPU_arch: v7E-M
  Tag_CPU_arch_profile: Microcontroller
  Tag_THUMB_ISA_use: Thumb-2
  Tag_FP_arch: VFPv4-D16
  Tag_ABI_PCS_RW_data: PC-relative
  Tag_ABI_PCS_RO_data: PC-relative
  Tag_ABI_PCS_GOT_use: GOT-indirect
  Tag_ABI_FP_denormal: Needed
  Tag_ABI_FP_exceptions: Needed
  Tag_ABI_FP_number_model: IEEE 754
  Tag_ABI_align_needed: 8-byte
  Tag_ABI_align_preserved: 8-byte, except leaf SP
  Tag_ABI_HardFP_use: SP only
  Tag_ABI_VFP_args: VFP registers
  Tag_ABI_optimization_goals: Prefer Speed
  Tag_CPU_unaligned_access: v6
  Tag_FP_HP_extension: Allowed
  Tag_ABI_FP_16bit_format: IEEE 754

gdb

だいたい -q -iex “set auto-load safe-path ./” で、もろもろは.gdbinitに書いてる。

リンカのメモリコマンド

大半のリンカスクリプトでは、リンク先のようにメモリコマンドに (rwx) のように記述されている。(stm32のやつとか)

リンカのメモリコマンドの仕様は以下のようになっている。

https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/4/html/Using_ld_the_GNU_Linker/memory.html

The attr string must consist only of the following characters:

R Read-only section

W Read/write section

X Executable section

おそらく、多くの人が r=read, w=write という風に誤解しているのだろう。

LDCでのvolatileのはなし

LDCはpragmaを利用してLLVMのコード生成時にvolatileLoad/volatileStoreのハンドリングをしてる。

ここではvolatileLoadの呼び出し/コード生成までみていく。

core.bitopモジュールは、LDCコンパイラを使うときは特に指定しなくても pragma(LDC_intrinsic, “ldc.bitop.vld”) を指定して volatileLoad の宣言をしている。

// druntime - src/core/bitop.d
version (LDC)
{
    pragma(LDC_intrinsic, "ldc.bitop.vld")
        ubyte volatileLoad(ubyte* ptr);
    pragma(LDC_intrinsic, "ldc.bitop.vld")
        ushort volatileLoad(ushort* ptr);
    pragma(LDC_intrinsic, "ldc.bitop.vld")
        uint volatileLoad(uint* ptr);
    pragma(LDC_intrinsic, "ldc.bitop.vld")
        ulong volatileLoad(ulong* ptr);

    pragma(LDC_intrinsic, "ldc.bitop.vst")
        void volatileStore(ubyte* ptr, ubyte value);
    pragma(LDC_intrinsic, "ldc.bitop.vst")
        void volatileStore(ushort* ptr, ushort value);
    pragma(LDC_intrinsic, "ldc.bitop.vst")
        void volatileStore(uint* ptr, uint value);
    pragma(LDC_intrinsic, "ldc.bitop.vst")
        void volatileStore(ulong* ptr, ulong value);
}
...

宣言の一覧はこれ。

// ldc - gen/dpragma.d
extern (C++) enum LDCPragma : int {
  LLVMnone = 0,   // Not an LDC pragma.
  LLVMignore, // Pragma has already been processed in DtoGetPragma, ignore.
  LLVMintrinsic,
  LLVMglobal_crt_ctor,
  LLVMglobal_crt_dtor,
  LLVMno_typeinfo,
  LLVMalloca,
  LLVMva_start,
  LLVMva_copy,
  LLVMva_end,
  LLVMva_arg,
  LLVMinline_asm,
  LLVMinline_ir,
  LLVMfence,
  LLVMatomic_store,
  LLVMatomic_load,
  LLVMatomic_cmp_xchg,
  LLVMatomic_rmw,
  LLVMbitop_bt,
  LLVMbitop_btc,
  LLVMbitop_btr,
  LLVMbitop_bts,
  LLVMbitop_vld,
  LLVMbitop_vst,
  LLVMextern_weak
};

Dコードから命令に落とすところはこうなっている。素朴。

bool DtoLowerMagicIntrinsic(IRState *p, FuncDeclaration *fndecl, CallExp *e,
                            DValue *&result) {
...
  if (fndecl->llvmInternal == LLVMbitop_vld) {
    if (e->arguments->dim != 1) {
      e->error("bitop.vld intrinsic expects 1 argument");
      fatal();
    }
    // TODO: Check types

    Expression *exp1 = (*e->arguments)[0];
    LLValue *ptr = DtoRVal(exp1);
    result = new DImValue(e->type, DtoVolatileLoad(ptr));
    return true;
  }
...

ここでLLVMの中間コード生成するところに渡している。あとはごにょごにょやってLLVM IRを生成してるだけ。(だけ、とは)

LLValue *DtoVolatileLoad(LLValue *src, const char *name) {
  llvm::LoadInst *ld = gIR->ir->CreateLoad(src, name);
  ld->setVolatile(true);
  return ld;
}

たとえば、ARM Cortex-Mとかでベアメタル環境で volatileLoad/volatileStore がほしければ core.bitop の定義をそのままもってくるとよい。