ちょっと早い夏休みだ自由研究をしよう(1日目)
TL;DR
- TRIMという会話で不整合が起きることがたまにある。
- FS TRIMと、MMC TRIMとは別概念。そこを注意して会話をしましょう。
- FS TRIM : ファイルシステムの未使用ブロックの探索と開放。
- MMC TRIM : 不要ブロックの開放
■ はじめに
- eMMC周りの仕事をしていると、「erase」「trim」「discard」」って言葉をよく聞く。
- なんか会話をしていると、どうにも話がかみ合わないことがある。
- 何となく雰囲気で使ってきたので、ちょっと時間を使って、それぞれの言葉の意味を整理したいと思う。
■ 結論
- file systemに対するTRIM指示とは、「割り当てられたブロック内で、未使用ブロックに対する解放を要求」。
- 使用中ブロックについては何もしない。
- ファイルシステムが考えている「未使用」部分である。
- 例えば、1度確保した領域を解放した部分など。
- mmc driverに対するTRIM指示とは、「指定ブロックに対する破棄の要求」。
- 指定されたブロックは利用中であろうとなかろうと失われる。
- TRIMを実行できる条件(Host and Card)だった場合にはMMC TRIMを実行。
- TRIMを実行できない条件(Host and/or Card)だった場合にはMMC ERASEを実行。
Application Layer
- ユーザーは、file systemに明示的にTRIM指示できる(fstrim / ioctl FITRIM)。
- ユーザーは、block deviceに明示的にDISCARD指示できる(blkdiscard)
Kernel Layer
- file systemは、TRIM指示を受けると未使用ブロックを再利用可能にするために、block deviceに破棄(DISCARD)を指示する。
- block deviceはDISCARD指示を受けると、mmc driverにDISCARD指示を出す。
- mmc driverはDISCARD指示を受けると、trimをハードウェアとしてサポートしているならばMMC TRIMを実行する。
サポートしていない場合には、もしくは、MMC ERASEを実行する。
■ コマンド
blkdiscard
- blkdiscard - discard sectors on a device
- https://man7.org/linux/man-pages/man8/blkdiscard.8.html
- https://github.com/karelzak/util-linux/blob/master/sys-utils/blkdiscard.c
- (memo) zero fill / discard / secure discardができる
- call sequence
- main()
- case 1)
- => ioctl(fd, BLKZEROOUT, &range)
- case 2)
- => ioctl(fd, BLKSECDISCARD, &range)
- case 3)
- => ioctl(fd, BLKDISCARD, &range)
fstrim
- fstrim - discard unused blocks on a mounted filesystem
- https://man7.org/linux/man-pages/man8/fstrim.8.html
- https://github.com/karelzak/util-linux/blob/master/sys-utils/fstrim.c
- (memo) FITRIMすることでdiscardする
- call sequence
- main()
- case 1:
- => fstrim_all()
- => fstrim_all_from_file()
- => fstrim_filesystem()
- => ioctl(fd, FITRIM)
- case 2:
- => fstrim_filesystem()
- => ioctl(fd, FITRIM)
■ Linux Kernel
ext4 FITRIM
- https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/fs/ext4/ioctl.c?h=v5.13.6#n1047
- call sequence
- ioctl(fd, FITRIM)
- => ext4_trim_fs()
- => ext4_trim_all_free()
- => ext4_trim_extent()
- => ext4_issue_discard()
- case 1:
- => __blkdev_issue_discard()
- case 2:
- => sb_issue_discard()
block device
- https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/block/ioctl.c?h=v5.13.6#n455
- call sequence
- blkdev_common_ioctl()
- case BLKDISCARD:
- blk_ioctl_discard(bdev, mode, arg, 0);
- => blkdev_issue_discard()
- => __blkdev_issue_discard()
- => bio_set_op_attrs(bio, REQ_OP_DISCARD, 0);
- case BLKSECDISCARD:
- blk_ioctl_discard(bdev, mode, arg, BLKDEV_DISCARD_SECURE);
- => blkdev_issue_discard()
- => __blkdev_issue_discard()
- => bio_set_op_attrs(bio, REQ_OP_SECURE_ERASE, 0);
- case BLKZEROOUT:
- blk_ioctl_zeroout(bdev, mode, arg);
- => blkdev_issue_zeroout()
- => __blkdev_issue_write_zeroes or __blkdev_issue_zero_pages
mmc driver
- https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/drivers/mmc/core/block.c?h=v5.13.6#n2209
- mmc_blk_mq_issue_rq()
- case REQ_OP_DISCARD)
- => mmc_blk_issue_discard_rq(mq, req);
- (ERASEの場合)
- Step1: mmc_switch(INAND_CMD38_ARG_ERASE)
- Step2: mmc_erase(MMC_TRIM_ERASE)
- => mmc_do_erase()
- => Step1) Execute ( MMC_ERASE_GROUP_START)
- => Step2) Execute ( MMC_ERASE_GROUP_END)
- => Step3) Execute ( MMC_ERASE, MMC_ERASE_ARG)
- (TRIMの場合)
- Step1: mmc_switch(MNAND_CMD38_ARG_TRIM)
- Step2: mmc_erase(MMC_TRIM_ARG)
- => mmc_do_erase()
- => Step1) Execute ( MMC_ERASE_GROUP_START)
- => Step2) Execute ( MMC_ERASE_GROUP_END)
- => Step3) Execute ( MMC_ERASE, MMC_TRIM_ARG)
- case REQ_OP_SECURE_ERASE)
- => mmc_blk_issue_secdiscard_rq(mq, req);
- (SECURE_ERASEの場合)
- Step1: mmc_switch(INAND_CMD38_ARG_SECERASE)
- Step2: mmc_erase(MMC_SECURE_SECERASE_ARG)
- => mmc_do_erase()
- => Step1) Execute ( MMC_ERASE_GROUP_START)
- => Step2) Execute ( MMC_ERASE_GROUP_END)
- => Step3) Execute ( MMC_ERASE, MMC_SECERASE_ARG)
- (SECURE_TRIMの場合)
- Step1: mmc_switch(INAND_CMD38_ARG_SECTRIM1)
- Step2: mmc_erase(MMC_SECURE_TRIM1_ARG)
- => mmc_do_erase()
- => Step1) Execute ( MMC_ERASE_GROUP_START)
- => Step2) Execute ( MMC_ERASE_GROUP_END)
- => Step3) Execute ( MMC_ERASE, MMC_SECURE_TRIM1_ARG)
- Step3: mmc_switch(INAND_CMD38_ARG_SECTRIM2)
- Step2: mmc_erase(MMC_SECURE_TRIM2_ARG)
- => mmc_do_erase()
- => Step1) Execute ( MMC_ERASE_GROUP_START)
- => Step2) Execute ( MMC_ERASE_GROUP_END)
- => Step3) Execute ( MMC_ERASE, MMC_SECURE_TRIM2_ARG)