来源:https://www.ichenfu.com/2022/10/05/enable-trim-on-usb-attached-scsi-ssds/
最近折腾了一小段时间的PCDN,家里刚好有一个闲置的JetsonNano和一块闲置的SSD,刚好可以跑跑PCDN,每天挣个宽带钱。具体跑的哪家,就不说了,说说在这过程中遇到的一个小问题:一般来说,PCDN或者类似的业务,对磁盘的写入压力还是比较大的,虽然可能平均的写入带宽并不高,但是也架不住每天读写的时间相当长,虽然我这块SSD是闲置的,但好歹是个传家宝,不管怎么说,还是有那么点点心疼的,肯定是不太希望哪天这SSD被写坏了。
在这种场景下,尽可能延长SSD的写入寿命就很重要了,而方法之一呢,就是想办法把SSD的Trim
命令给用上。
用上Trim
命令之前,可以先简单了解一下背后的逻辑,具体的可以参考Wiki,简单来说呢,因为SSD依赖垃圾回收机制来平衡NAND的磨损,但是呢具体到一整个LBA空间,只有文件系统知道哪些数据块是有效数据,所以就需要通过Trim
命令,建立文件系统空闲空间和SSD底层数据块的关联,从而让SSD的主控更好的进行垃圾回收操作,一般来说,合理的使用Trim,可以有效的提高SSD的性能和寿命。当然了,Trim
命令是ATA指令集里的,也就是SATA接口SSD才会有,对于SCSI以及SAS接口SSD,还有NVMe SSD来说,也有相应的UNMAP
和Deallocate
指令,作用都是一样的。
一般来说,在Linux下,一个设备是否支持Trim
操作,可以通过lsblk --discard
进行查看,当输出中的DISC-GRAN
和DISC-MAX
列不为0时,说明这个设备是支持Trim
操作的:
jetson-nano:chenfu:# lsblk --discard
NAME DISC-ALN DISC-GRAN DISC-MAX DISC-ZERO
sda 0 0B 0B 0
mmcblk1 0 4M 76M 0
└─mmcblk1p1 0 4M 76M 0
比如在我这个JetsonNano上,可以看到我外接的这块SSD硬盘,对应sda
设备是不支持Trim的,但是mmcblk1
这个设备,也就是装系统用的一个小的MicroSD卡是支持的。
那么问题来了,针对上面的输出,sda这块盘是不支持Trim的,那怎么样才能让他支持呢?
首先需要明确的是,因为这块盘是我通过一块USB移动硬盘盒转接到板子上的,也就意味着这块硬盘并没有用原生的SATA接口(当然这块开发版本身也不支持SATA接口)。而对于移动硬盘盒而言,将SATA口转换成USB口,会需要一个桥接芯片进行协议的转换,那么桥接芯片是否支持Trim命令的转换,就显得非常重要了。对于一些老的移动硬盘盒,大多使用的是Mass Storage Class Bulk-Only Transport (BOT)这个协议,但是对于一些比较新的桥接芯片,基本都会支持一个新的叫做USB Attached SCSI Protocol (UASP) 的新协议。所以我也查了一些资料,同样也是结合产品页的一些宣传,买了一个支持UAS协议的移动硬盘盒,根据评论看,这个硬盘盒是支持Trim的,但是大部分用户似乎都是在Windows下进行测试的,在Linux下是否真的支持,是否需要新版本内核或者驱动的支持还不知道。
等硬盘盒到手,插上之后系统lsusb看了一下:
jetson-nano:~:% lsusb
Bus 002 Device 002: ID 174c:225c ASMedia Technology Inc. Ugreen Storage Device
VendorId是0x174c,也就是ASMedia公司的桥接芯片,但是225c这个ProductId并没有在USB ID数据库里查到,不过从数据库里看0x1153这个ProductId对应ASM1153这款芯片来说,那225c应该是对应着ASM225CM这个芯片?从目前的资料看,这个芯片理论上是支持Trim的,至少可以通过刷新固件来解决支持的问题。
然而系统识别出sda之后,lsblk --discard
依然提示不支持Trim。
于是又搜索了一些资料,终于在Arch的SSD Wiki里找到了一些信息:
其实现在一些USB转SATA芯片(如VL715、VL716等)以及在外接NVMe硬盘盒(如IB-1817M-C31)中使用的USB转PCIe芯片(如 智微(JMicron) JMS583 )支持类似TRIM的命令。这些命令可通过 USB Attached SCSI 驱动程序(在Linux下称为”uas”)发送。然而内核可能不会自动检测到并启用这一功能。
会不会是因为芯片是支持的,但是系统默认没有开启呢?于是按Wiki里的说法,使用sg_readcap -l /dev/sda
命令读取设备的标志位:
jetson-nano:chenfu:# sg_readcap -l /dev/sda
Read Capacity results:
Protection: prot_en=0, p_type=0, p_i_exponent=0
Logical block provisioning: lbpme=0, lbprz=0
Last LBA=937703087 (0x37e436af), Number of logical blocks=937703088
Logical block length=512 bytes
Logical blocks per physical block exponent=0
Lowest aligned LBA=0
Hence:
Device size: 480103981056 bytes, 457862.8 MiB, 480.10 GB
发现Logical block provisioning: lbpme=0, lbprz=0
其中lbpme=0,因为LBPME位为0,所以内核默认是不会开启DISCARD的支持。针对这种情况,还需要继续通过sg_vpd -a /dev/sda
命令查询设备支持的命令情况:
jetson-nano:chenfu:# sg_vpd -a /dev/sda
Supported VPD pages VPD page:
...
Unit serial number VPD page:
Unit serial number: 704108E11D02
Device Identification VPD page:
Addressed logical unit:
designator type: NAA, code set: Binary
0x5000000000000001
Block limits VPD page (SBC):
Write same non-zero (WSNZ): 0
...
Block device characteristics VPD page (SBC):
Non-rotating medium (e.g. solid state)
...
Logical block provisioning VPD page (SBC):
Unmap command supported (LBPU): 1
Write same (16) with unmap bit supported (LBPWS): 0
Write same (10) with unmap bit supported (LBPWS10): 0
Logical block provisioning read zeros (LBPRZ): 0
Anchored LBAs supported (ANC_SUP): 0
Threshold exponent: 0 [threshold sets not supported]
Descriptor present (DP): 0
Minimum percentage: 0 [not reported]
Provisioning type: 0 (not known or fully provisioned)
Threshold percentage: 0 [percentages not supported]
可以发现在Logical block provisioning VPD page (SBC)
段下,有Unmap command supported (LBPU): 1
,说明设备本身是支持Unmap指令的,因为前面说到,ATA中的Trim其实就是对应的SCSI中的UNMAP,所以支持UNMAP也就是支持了Trim,当然这中间的转换过程,应该是有硬盘盒的主控来完成。
那既然在物理上是支持Trim的,那剩下的就是逻辑上怎么启用的问题了,先看下目前内核识别的设备的provisioning_mode:
jetson-nano:chenfu:# cat /sys/block/sda/device/scsi_disk/0:0:0:0/provisioning_mode
full
可以发现输出是full
,也就是说内核当前是没有检测到设备支持Trim特性,解决方法也比较简单,直接echo unmap
到这个文件:
jetson-nano:chenfu:# echo unmap > /sys/block/sda/device/scsi_disk/0:0:0:0/provisioning_mode
jetson-nano:chenfu:# lsblk --discard
NAME DISC-ALN DISC-GRAN DISC-MAX DISC-ZERO
sda 0 512B 4G 0
mmcblk1 0 4M 76M 0
└─mmcblk1p1 0 4M 76M 0
可以看到,强制指定provisioning_mode为unmap之后,lsblk --discard
的输出已经提示sda设备支持Trim了。
最后,为了能让这个特性可以在插入硬盘盒的时候自动生效,可以手动编写一个Udev的规则文件:
echo 'ACTION=="add|change", ATTRS{idVendor}=="174c", ATTRS{idProduct}=="225c", SUBSYSTEM=="scsi_disk", ATTR{provisioning_mode}="unmap"' >>/etc/udev/rules.d/10-uas-discard.rules
也就是说,当有idVendor为174c,idProduct为225c的设备(也就是我的这个硬盘盒)连接的时候,自动设置provisioning_mode为unmap。