Discard over USB

The aim of this article is to Article description::enable discard/trim operational for block devices connected via the systems USB bus.

What it does
Trim and discard are two different names for the same thing.

The mount option is called discard, the command does the same thing but not as it happens. Both inform the underlying block device about space that the filesystem was using but has been returned to the free pool. In general this is a good thing as FLASH based devices can only change memory cells in one direction. They must be erased before they can be rewritten. Erase is a slow operation, much slower than write, so discard gives the block device the information it needs to erase the discarded blocks in good time before they need to be rewritten. This in turn maintains the write performance of the device.

Why is USB special
With NVME or SATA connected FLASH devices, trim/discard just works. When a USB adapter is involved trim is usually disabled by default and need to be enable before it can be used.

Readers who have moved a FLASH based block device to a USB enclosure may have noticed a dmesg warning "Discard is not supported".

Prerequsites

 * A USB3 link to the FLASH device. USB2 cannot support trim.
 * A USB3 to FLASH bridge that actually supports trim

Testing that trim is supported
To get the required tools:

Find the USB attached storage
Plug the drive in and inspect the end of dmesg.

If there is only one drive it will be.

The remainder of this document will use. Readers are expected to adjust that to suit their own individual circumstances.

Investigate support
/sys/devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb2/2-2/2-2:1.0/host0/target0:0:0/0:0:0:0/scsi_disk/0:0:0:0/provisioning_mode:full

Notice the  section in the prior output. That shows that trim is disabled even if its supported. We need it to show unmap.

Block limits VPD page (SBC): Write same non-zero (WSNZ): 0 Maximum compare and write length: 0 blocks [Command not implemented] Optimal transfer length granularity: 1 blocks Maximum transfer length: 65535 blocks Optimal transfer length: 65535 blocks Maximum prefetch transfer length: 65535 blocks Maximum unmap LBA count: 4194240 Maximum unmap block descriptor count: 1 Optimal unmap granularity: 1 blocks Unmap granularity alignment valid: false Unmap granularity alignment: 0 [invalid] Maximum write same length: 0 blocks [not reported] Maximum atomic transfer length: 0 blocks [not reported] Atomic alignment: 0 [unaligned atomic writes permitted] Atomic transfer length granularity: 0 [no granularity requirement Maximum atomic transfer length with atomic boundary: 0 blocks [not reported] Maximum atomic boundary size: 0 blocks [can only write atomic 1 block]

The Unmap entries are important here. Make a note of the Maximum unmap LBA count: Will need it later.

A report like Maximum unmap LBA count: 0 [Unmap command not implemented] Maximum unmap block descriptor count: 0 [Unmap command not implemented] Optimal unmap granularity: 0 blocks [not reported] means that trim is not supported, so stop there.

Read Capacity results: Protection: prot_en=0, p_type=0, p_i_exponent=0 Logical block provisioning: lbpme=0, lbprz=0 Last LBA=500118191 (0x1dcf32af), Number of logical blocks=500118192 Logical block length=512 bytes Logical blocks per physical block exponent=0 Lowest aligned LBA=0 Hence: Device size: 256060514304 bytes, 244198.3 MiB, 256.06 GB

We need the Logical block length from that output. Make a note of it.

Enabling trim
Change the provisioning_mode to unmap

Using the Maximum unmap LBA count and Logical block length, multiply the two numbers together to arrive at the discard_max_bytes for the device.

Set this with

Verify That the changes happened by checking for provisioning_mode:unmap.

/sys/devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb2/2-2/2-2:1.0/host0/target0:0:0/0:0:0:0/scsi_disk/0:0:0:0/provisioning_mode:unmap

provisioning_mode:disabled indicates that trim is not supported. Revert provisioning_mode to full and stop.

That's trim enabled until the next boot.

Testing trim support
Be sure that the filesystem(s) to be trimmed are mounted.

This will list each filesystem with the space that it trimmed. It will be slow the first time as the entire free space pool will be trimmed. We have to trust that the drive will not erase already erased space.

Auto enabling trim
The above manual changes will not be preserved across reboots. (e)udev users need to write a rule.

Discover the the Vendor and Device ID of the USB device that represents the FLASH device.

Here, the SATA 6Gb/s bridge makes it easy to spot. The ID required is ID 174c : 55aa. That's the VendorID and DeviceID parts in order.

Use that in a udev rule

ACTION=="add|change", ATTRS{idVendor}=="", ATTRS{idProduct}=="", SUBSYSTEM=="scsi_disk", ATTR{provisioning_mode}="unmap"}}

Reboot to test. should trim all the filesystem again.

Closing notes
There is a debate over using the discard option in /etc/fstab or running fstrim in a weekly or monthly cron job. The debate centres around the write amplification caused by performing needless erases cycles. As its non trivial to investigate the trim behaviour of individual devices, this author recommends either the cron job approach or making fstrim part of the routine tidying up after a @world update.