テストステ論

高テス協会会長が, テストステロンに関する情報をお届けします.

SSDにおけるTRIMやSecure Eraseの調査

dm-lcの測定をするに当たって, SamsungSSDを買いました. 測定では, 測定前に毎回SSDをクリーンにする必要があります. クリーンにするとは, SSDが管理しているデータをすべて無効化して, Garbage Collectionが当面起こらないようにすることです. 詳しくは, Write Amplifierなどを読めばよいのだと思います. 一言でいうと, SSDは, ややこしいという話です.

hdparmでSecure Eraseしてみる

とりあえず, Secure Eraseをすることにしました. LinuxでSecure Eraseを実行するには, ここに書かれているように, hdparmに--security-erase optionをつけて実行すれば良いです. そのための前提としては, hdparm -Iで見た時に, SSDが "not frozen" であることが必要です. しかし, 私のSSDは "frozen"でした. まるで, 私の心のようです.

root@Lyle:/home/akira# hdparm -I /dev/sdd

Security:
        Master password revision code = 65534
                supported
        not enabled
        not locked
                frozen <<
        not expired: security count
                supported: enhanced erase
        2min for SECURITY ERASE UNIT. 2min for ENHANCED SECURITY ERASE UNIT.

frozenというのは, 「BIOSが, このSSDは誤ってデータが消去されないようにロックしてるよ」という意味です. このマシンについている他のディスクもfrozenです. BIOSが悪さをしていることは間違いないでしょう. というわけで, BIOSを設定すれば何とかなりそうなものと思いました. しかし設定の仕方が良くわかりません.
仕方なく, 別の方法をとることにしました. それは, 「初期化中はドライブを認識させないで, 立ち上がってからドライブをホットプラグする」という方法です. この方法を使うと, うまく行きます. しかし, 再起動するとやっぱりfrozenに戻っているので, この方法は破棄しました. だって, 測定ごとに毎回ホットプラグするのはめんどくさいでしょう.

Security:
        Master password revision code = 65534
                supported
        not enabled
        not locked
        not frozen << OH!
        not expired: security count
                supported: enhanced erase
        2min for SECURITY ERASE UNIT. 2min for ENHANCED SECURITY ERASE UNIT.

ioctlでdiscardしようと思うがめんどくさい

カーネル内でblkdev_issue_discardというのを使うと, ディスクをdiscard出来るということは知ってましたので, ioctlでも出来るだろうと思って調べると, BLKDISCARDで出来るということが分かりました. C言語でやるのはめんどくさいので, Pythonでやろうかと思いました. ioctlを実行するコマンドを作って, PyPIにうpするかとか思いました. しかしそんなしょうもないことはしたくない. 誰かやってないのか!PyPIではヒットなし, aptitudeでもヒットなし, Gemでもたぶんなし. fstrimというコマンドは見つけましたが, これはファイルシステムに対して実行するものであり, ブロックデバイスには使えそうもない. なぜだ!しかし神は私をお見捨てにならなかった*1

blkdiscardコマンドを使ってみましょう

[PATCH] blkdiscard: add new command
blkdiscard is used to discard device sectors. This is useful for solid-state drivers (SSDs) and thinly-provisioned storage. Unlike fstrim this command is used directly on the block device.

まさにこれが欲しかった. util-linuxというのは, linuxユーザランドコマンドを充実させていくプロジェクトであり, 例えば, dmesgやmountといったコマンドもutil-linuxのものです. 2012の9月にパッチされたものであり, Debianに入ってるutil-linuxでは手に入りません. upstreamのコードをビルドする必要があります. 落としてきて, ./autogen.shすればautotoolsの処理は自動で終わるのでインストールは簡単です.

早速使ってみましょう. まずはhelpです. なぜか, 数値の単位がbyteになっています. blockdev --getsizeなどが返す数値の単位がsector(512byte)なので, それに合わせて欲しかったなぁと思います. パッチを書いたのはRedhatの人ですが, Redhatの人も実力がまちまちだなぁという印象です. この人のポジションなら奪えると思います.

root@Lyle:/home/akira/src/util-linux# /usr/local/util-linux/sbin/blkdiscard --help

Usage:
 blkdiscard [options] <device>

Options:
 -o, --offset <num> offset in bytes to discard from
 -l, --length <num> length of bytes to discard from the offset
 -s, --secure perform secure discard
 -v, --verbose print aligned length and offset

 -h, --help display this help and exit
 -V, --version output version information and exit

-sというのをつけるとsecure discardになるそうです. まずは付けずにやってみましょう. straceで見てみると, どうやらちゃんと動いているように見えます.

open("/dev/sdd", O_WRONLY) = 3
ioctl(3, BLKGETSIZE64, 0x7fffb4235028) = 0
ioctl(3, BLKSSZGET, 0x7fffb4235020) = 0
ioctl(3, BLKDISCARD, 0x7fffb4235010) = 0

次は, -sをつけてみましょう. 失敗しました. frozenだから, Secure Eraseは受け付けないのです. Secure Eraseは出来ないのにdiscardは出来るという抜け道を残すBIOSは何たる間抜けなのでしょうか.

ioctl(3, BLKGETSIZE64, 0x7fff0b2c4858) = 0
ioctl(3, BLKSSZGET, 0x7fff0b2c4850) = 0
ioctl(3, 0x127d, 0x7fff0b2c4840) = -1 EOPNOTSUPP (Operation not supported)
write(2, "blkdiscard: ", 12blkdiscard: ) = 12
write(2, "/dev/sdd: BLKSECDISCARD ioctl fa"..., 36/dev/sdd: BLKSECDISCARD ioctl failed) = 36

BLKDISCARDとBLKSECDISCARDはどう違うのか?(カーネルコード内)

BLKDISCARDとBLKSECDISCARDがカーネル内でどう違った処理になってるのかが気になります. と思いましたが, 大した情報は得られませんでした. 単に, 最終的に, 発行するbioのフラグにREQ_SECUREがつくかつかないかくらいの違いです.

(blkdev_ioctl内. こいつ自身は, compat_blkdev_ioctlから呼ばれる)

        case BLKDISCARD:
        case BLKSECDISCARD: {
                uint64_t range[2];

                if (!(mode & FMODE_WRITE))
                        return -EBADF;

                if (copy_from_user(range, (void __user *)arg, sizeof(range)))
                        return -EFAULT;

                return blk_ioctl_discard(bdev, range[0], range[1],
                                         cmd == BLKSECDISCARD);
        }
static int blk_ioctl_discard(struct block_device *bdev, uint64_t start,
                             uint64_t len, int secure)
{
        unsigned long flags = 0;

        if (start & 511)
                return -EINVAL;
        if (len & 511)
                return -EINVAL;
        start >>= 9;
        len >>= 9;

        if (start + len > (i_size_read(bdev->bd_inode) >> 9))
                return -EINVAL;
        if (secure)
                flags |= BLKDEV_DISCARD_SECURE;
        return blkdev_issue_discard(bdev, start, len, GFP_KERNEL, flags);
}

(blkdev_issue_discard内)

        if (flags & BLKDEV_DISCARD_SECURE) {
                if (!blk_queue_secdiscard(q))
                        return -EOPNOTSUPP;
                type |= REQ_SECURE;
        }
        __REQ_SECURE, /* secure discard (used with __REQ_DISCARD) */

TRIM(discard)とSecure Eraseはどう違うのか

では, REQ_SECUREをつけるとどう違う?というのが気になります. 私には, TRIMをするのもSecure Eraseをするのも全く同じに見えます. 詳しいことは分かりませんが, 以下では「Secure Eraseはディスク全域に対するTRIMと同じだよ」と言われています. 私もそう思いますが, Secure Eraseの方が, ディスク自体を完全に初期化出来るので, FTLのテーブルを完全に再構築したりするということも出来るかも知れません. また, Secure EraseはHDDでも利用出来ます. ディスクを破棄する時にデータ消去することが目的です. そもそも比較することがおかしいものであるようにも思います. 今回は, TRIMが使えるようになったので十分と思います.

http://forums.whirlpool.net.au/archive/2076711
Secure erase is a drive-wide TRIM command. TRIM tells the SSD controller that sections of the drive (entire drive in this case) no longer keep relevant data and controller is free to do whatever it wants with those sections.

以上です. Secure Eraseから始まって, 色々と調査した結果をまとめました. 他, blkdev_issue_discardがファイルシステムから呼ばれている時の挙動をsystemtapで調査しましたというブログも参考にしました. なぜTRIMする必要があるのか, の理由も書いてあります.

*1:オーマイ天照大御神