2010年09月20日

FLASHの使い方(案)

MTDのパーティションについて

    細かく分けてしまうと、ウェアレベリングをそのパーティション内でやらざるを得ないという問題が出る。スワップは領域が小さい上に書き換え頻度が多くなることになり、持つこと自体が厳しくなる。そのため、大きな領域にして UBI パーティションとして、その後の分割は後で考えることにしたい。

    UBI 以外は、ブート用と カーネル用のパーティションに分ける。
    ただし、使うのは先頭だけで、隙間をたっぷり取っておく。(
    隙間の使い道はあとで考える。)

      4MB ブート用 (代替ブロック 2)
      60MB カーネル用 (代替ブロック 2)
      残り UBI 用 (代替ブロック 10)

    こんなところでどうだろう。

と書いた。これについてもう少し考察しておきたい。

    最新の案:

    4MB ブート用 (代替ブロック 2)
    60MB カーネル用 (代替ブロック 2)
    残り UBI 用 (代替ブロック 200 for 4Kpage,2GB)




E7002(tcc8902) のファームウェアアップデート方法についてちょっと調べた。ポイントだけ書くと、

  • FWDN というツールでアップデートするが、tcc8902 の USB boot にも対応している。
  • 書き込むデータはいくつかに分割されている。その種類は、
    Boot Loader , Kernel Image, Ramdisk File, MTD(tcc8900_mtd.img :121MB)
  • >MTD については、NAND Data.fai (730KB) というファイルも必要。

FWDN が MTD を外部から書くのだとすれば、バッドブロック処理は必須で、そのための情報が NAND Data.fai のような気がする。(違うかも知れない)。
Boot Loader , Kernel Image, Ramdisk File についても バッドブロック処理があるのかも知れないが、簡単なものではないかと思える。

いま作ろうとしているものも事情は同じ。上記も参考にしよう。

まず、ブート用、カーネル用 の書き換え頻度は少ない。 バッドブロックが新たにできる確率は小さい。それでも 初期状態ですら存在する確率は 0 ではないので、対処だけはしておく。

対処は、簡単なもので良く、mtd ドライバのバッドブロックに対応すれば十分。対応は、書き込みツールと Boot Loader など読み込むコードで行う。Linux の方法を採用しているだけだから、Linux 自体は なにもしなくて良い。

さて問題は、UBI 用である。手持ちの SSD は、32GB/2Kブロック (16K 個のブロック)に対して、1000個ほどの代替ブロックを用意している。2GB/512B ブロック なら 4K 個のブロック数なので、250 個ほどは 代替ブロックが必要という計算になる。にもかかわらず 10個ほどしか用意しないのは、mtd の仕組みは 使わないと思っているから。

問題は バッドブロックだけではない。たとえ再インストールしても いままでの書き換え回数の情報は残したい。

で、それをするためには、UBI は、Linux で必ず更新する..ということにしまうのが一番簡単である。

インストールする際にも、Linux を動かして Linux が自分で UBI を初期化することになる。

そう決めたときの課題は、インストールする内容をどこから持ってくるか。

  • USB から
    インストールは、USBBOOT だけで行なおうと考えている。それならば自由度は高く USBBOOT 経由でデータを転送することが可能ではある。

    ただそうした場合、ドライバーを作成しなくてはならない。これは大分面倒。

      もうひとつは、microSD/SD から。

      これは、どこに置くか決めるだけで面倒ではない。だが、操作のほうが面倒だ。データは PC にあるので、microSD/SD にいちいち書きこまなくてはならない。

      スターンドアローンで アップデートできるなら microSD/SD に置いても良いのだが.. 一部は PC / 残りは microSD/SD というのは避けたい。

    • カーネル用パーティションから

      これが一番簡単。ただ、容量が足りなくなるのが心配だし、容量が無駄に使われるのが嫌な点。

      足りなくなる点については、少し考察してみよう。オリジナルのファームウェアは、圧縮して 20-30MB 。android 2.1(tcc8902) は、60MB ほど。ubuntu を採用した SmartQ5 では、170MB ほど。

      android ですら 60MB なら、個人が用意できる基本ファームウェアがそれ以上になるとは思えない。それでも超える場合は、上の方法を使うということでどうだろう。

    まぁ.. こういうわけで、圧縮したイメージを カーネル用パーティションの隙間におくことにしよう。

    次の問題は、インストーラ。インストーラ用のカーネル と initrd を別に持つというのもひとつの方法だが、正規の initrd に 組み込んでしまうのはどうか?

      あるボタンを押して立ち上げれば、initrd がそれを認識して、インストールを行う。初期化できたところで、正規の mount 処理を動かす。

    こんなかんじ。新しくなっていれば、自動的にインストールするのでも良い。

    どうしても panic するという自体になってしまったら、初期化すれば立ち上がる。

    ただ、ユーザのデータと その下位構造の UBI は勝手に壊さないようにする。ユーザのデータや UBI まで壊れた場合は、確認を求める。No! の場合、その場は電源を切る。後で USBBOOT を使った修復ツールを動かすとか別途考える。

    パーティションの容量配分は、これで良いか?


      4MB ブート用 (代替ブロック 2)
      60MB カーネル用 (代替ブロック 2)
      残り UBI 用 (代替ブロック 10)


    と書いたが、これで良いのだろうか? まず、代替ブロックとはどういう単位で代替するのか? 代替ブロックのために 容量がどれだけ犠牲になるのか知らないといけない。

      ちょっとコードを見てみる。代替ブロックの定義は、get_jz_badblock_table() を call することで取得する。

      これは、mtdblock-jz.c の mtdblock_zone_init() で行っている。
      で、reserved_sectors という変数に 得た値 を (phys_erase_shift - 9) だけ左シフトして格納している。

      どうも reserved_sectors は 512B 単位で 代替ブロックの単位は、消去ブロックサイズ (4Kpage なら 512K)らしい。

        そうなると 4MB の ブート用パーティションは 1MB 取られていることになる。

      次に管理領域。
       total_virt_block = total_phys_block - bad_block_num
      (単位:消去ブロック, total_phys_block は、mtd の size(バイト単位) を phys_erase_shift している。)
      となっているから、reserved_sectors に管理領域が含まれているようだ。

      さて、エラーを見つけたら マークを付けるようだ。これは nand_base.c の nand_default_block_markbad() 。見ると OOB に対して 2 バイト分 上書き?している。

      それはともかく、それをどう変換するのか .. 関数は、mtdblock-jz.c の mtdblock_address_translate() らしい。これは block_lookup[] という配列を使って virt → phys の変換をしている。512KB 単位なら 4GB でも 8K エントリで 32KB しか消費しないからこれで良いのだろう。 --- 問題はそれをどう作っているか? ということになる。

      どうも nandblock-jz.c の mtdblock_block_lookup_map_entry() というのが更新する関数らしい。そして、badblock を見つけたときに どこを割り当てるかというのは、mtdblock_find_free_block() ということらしい。

      なにをしているかというと block_info[] を頭からサーチして、free block の マークが付いているものをさがしている。

      block_info[] をどう作っているのか調べないといけない。ちなみに、erase すると block_info[].lifetime を +1 している。

      このデータをいつどう書き戻しているのかというのは結構重要そう。
      block_info は、

      struct mtdblk_block_info {
      unsgined int lifetime;
      unsgined char tag; /* 0x01: FREE 0x02: BAD 0x04: EMPTY*/
      };

      という構造。mtdblock_zone_init で read_oob して メモリ上の配列として構成しているようだ。

    ここまでの理解は、

    • read エラーを見つけたら 消去ブロック単位で代替する。
    • 代替ブロックは oob を線形サーチすれば見つかる。(管理のための領域は、データブロックにはない)

    カーネル用の領域は、こういう仕組みが役に立つだろう。普通なら 5% の代替エリアが必要だとすれば、6 個ぐらいあった方が良い。書き換え頻度が少ないが 代替の単位が大きいので 2 個では不安なのだが、これで行くことにする。

    それに対して ブート用はどうだろう? カーネルでの書き換えは 4Kpage の場合 512KB 単位になるわけで、4MB で 8 つの エリアがある。壊れれば代替してくれるわけだが、先頭が壊れた場合 代替してくれても困るかもしれない。でも、なにか物理的な先頭ページにアクセスする手段があるのだろう。そうだとすれば、壊れていた場合に mtdblock の先頭にアクセスすると、代替されて 無駄に 1 ブロック使われるという弊害がでるが、気にしないことにする。

    8 個のブロックのうち ブートローダが 1 個。代替に 2 個。5 つのエリア(2.5MB) は自由に使えることになる。

    5 つのうち 少なくとも 1 つはカーネルパラメータを入れたい。(
    2 つ 3 つあっても良い。)

    残りは、u-boot を使いたくなった場合に使うことにする。一応 2 個のエリア (1MB) 確保できているから大丈夫だろう。

    最後に UBI 用についてなのだが、512KB 単位で代替しているわけがない。10 個あっても UBI を通して使う限り関係ないはず。

    mtdblock としてアクセスした場合に 代替されてしまうかも知れないのだが、そういう場合も、UBI を通せば関係ないような気がする。

    というわけで UBI もこのままで良いだろう。

      追記: どうも良くないようだ。

      badblock の処理は 下位レイヤに従う。... どうも badblock が出来にくくするために ウェアレベリンングを行うのであって、badblock が出来てしまえばすなおに代替するようだ。

      そうであれば 5 % の 200 個 必要そうだ。これは 4Kpage,2GB の場合で 2Kpage や 4GB なら倍の 400 個。


      実際の OOB の代替ブロックを示すデータについて:

      OOB に格納する形式は、

      struct mtdblk_fake_fsbuf {
      unsigned int block_addr_field1;
      unsigned int block_addr_field2;
      unsigned int lifetime;
      };

      という形式で、oob のオフセット 2 から格納されている 。
      block_addr_field1 , block_addr_field2 に virt_block 番号が入っていて、読み出したとき同じなら正しいデータと見做す。
      lifetime は 消去回数。

      このデータがどこにあるかというと、各消去ブロックの先頭ページに対する oob 。次のページから CONFIG_MTD_OOB_COPIES 分だけ同じデータがあり、壊れていたら コピーを見ることになっている。

      あと、代替ブロックが出来ていたら、代替ブロックの方を常に見ないと整合性が取れない。壊れているという mark はどこにあるのか?

      どうも nand_default_block_markbad() という関数でこの処理をやっているらしい。

      .. 見てみると、代替する場合は、各消去ブロック の 127ページ 目の oob の先頭から 2 バイトを 0 にする。判断は、先頭の 1 バイトが 0xff 以外だったとき 代替されていると判断する。
      (そういえば、バッドブロックのコードは、usbboot にもあった)

      これで、OOB の 先頭から 14 バイト分がどう使われるかは分かった。( そして 4Kpage で 8bit-BCH の場合は、 オフセット 24 から 最後まで ECC が入る。)

    (つづく)
posted by すz at 16:58| Comment(0) | TrackBack(0) | Jz47xx(Linux)
この記事へのコメント
コメントを書く
お名前: [必須入力]

メールアドレス: [必須入力]

ホームページアドレス: [必須入力]

コメント: [必須入力]

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
この記事へのトラックバックURL
http://blog.sakura.ne.jp/tb/40830660
※ブログオーナーが承認したトラックバックのみ表示されます。

この記事へのトラックバック