erase? trim? discard? の用語を整理してみる。

ちょっと早い夏休みだ自由研究をしよう(1日目)

TL;DR
=====

- TRIMという会話で不整合が起きることがたまにある。
- FS TRIMと、MMC TRIMとは別概念。そこを注意して会話をしましょう。
    - FS TRIM : ファイルシステムの未使用ブロックの探索と開放。
    - MMC TRIM : 不要ブロックの開放


■ はじめに
============

- eMMC周りの仕事をしていると、「erase」「trim」「discard」」って言葉をよく聞く。
- なんか会話をしていると、どうにも話がかみ合わないことがある。
- 何となく雰囲気で使ってきたので、ちょっと時間を使って、それぞれの言葉の意味を整理したいと思う。

■ 結論
======

- file systemに対するTRIM指示とは、「割り当てられたブロック内で、未使用ブロックに対する解放を要求」。
    - 使用中ブロックについては何もしない。
    - ファイルシステムが考えている「未使用」部分である。
        - 例えば、１度確保した領域を解放した部分など。
- 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を実行する。 <BR> サポートしていない場合には、もしくは、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)