Enabling TRIM on an external SSD on a Raspberry Pi

I've been doing a lot of benchmarking and testing with the Raspberry Pi 4 and SSDs connected via USB. I explored UASP Support, which USB SSDs are the fastest, and I'm now booting my Pis from USB SSDs.

Anyways, one thing that I have wondered about—and some people have asked me about—is TRIM support.

I'm working on a new video for my YouTube channel that will go into some more detail on which of the drives I tested support TRIM, but while I was researching for that video, I also found that TRIM support in Linux is not as simple as it seems at first glance—it's definitely not plug-and-play, in my experience.

While internal microSD cards seem to support TRIM out of the box, none of the external USB drives I tested supported it out of the box. They all needed a little help!

Much of the data in this post I attribute to this excellent comment by tom.ty89 on the Pi Forums.

What is TRIM? Why should I care about it?

This blog post is not going to get into the weeds on TRIM; I recommend this article on Crucial.com if you want to learn about it.

Does my SSD support TRIM?

In Linux, you can check if TRIM is currently supported by running one of the following commands (this blog post assumes you're booting off the SSD, so it's device /dev/sda—if you are not booting from the SSD you're checking, substitute accordingly!):

$ sudo fstrim -v /

If this reports back fstrim: /: the discard operation is not supported, then TRIM is not enabled.

You can also check with:

$ lsblk -D

If the DISC-MAX value is 0B, then TRIM is not enabled.

Now, being enabled and being supported in firmware are two different things. Some of my drives actually support TRIM even if it's not enabled out of the box.

For example, testing with my Corsair Flash Voyager GTX flash drive, I was able to determine the firmware supports TRIM, and I was then able to manually enable TRIM following tom.ty89's instructions.

Checking if the Firmware supports TRIM

To check if the device firmware supports TRIM, switch to the root user (otherwise you'll need to use sudo before most of the rest of the commands in this post), and install a couple utilities we'll need for the rest of this process:

$ sudo su
# apt-get install -y sg3-utils lsscsi

Run the following command and check the Maximum unmap LBA count:

# sg_vpd -p bl /dev/sda
Block limits VPD page (SBC):
...
  Maximum unmap LBA count: 4194240
  Maximum unmap block descriptor count: 1
...

Take note of it, then run the following command and check the Unmap command supported (LBPU):

# sg_vpd -p lbpv /dev/sda
Logical block provisioning VPD page (SBC):
  Unmap command supported (LBPU): 1
...

If the Maximum unmap LBA count is greater than 0, and Unmap command supported (LBPU) is 1, then the device firmware likely supports TRIM.

Warning: A few devices seemed to indicate they supported TRIM in firmware, like my Arcanite USB 3.1 Flash Drive... but when I tried enabling it I got a few errors. Then I tried running fstrim -v / on a whim, and ended up corrupting the drive's firmware, to the point it won't mount and can't be formatted anymore. So make sure you have a backup of any important data before you try on a drive that might not actually support TRIM!

Enabling TRIM

Now, if you know your device firmware supports TRIM, but it's just not in use currently, we can work on enabling TRIM.

Check on the current provisioning_mode for all drives attached to the Pi:

# find /sys/ -name provisioning_mode -exec grep -H . {} + | sort
/sys/devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb2/2-1/2-1:1.0/host0/target0:0:0/0:0:0:0/scsi_disk/0:0:0:0/provisioning_mode:full

We're going to need to change the provisioning_mode from full to unmap; but if you have more than one drive attached, you need to confirm which drive you need to change. You can do that using lsscsi:

# lsscsi
[0:0:0:0]    disk    Corsair  Voyager GTX      0     /dev/sda

Once you've confirmed which drive you need to change (in my case, there's only one, making this very easy), change the value from full to unmap in the path that the find command returned:

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

Run the find command again to confirm the provisioning_mode is now unmap.

Now, you need to update the discard_max_bytes value for the drive, based on the Maximum unmap LBA count value you got from the sg_vpd -p bl /dev/sda command earlier, times the Logical block length value you get from the sg_readcap -l /dev/sda command. In my case (your values may be different):

# echo $((4194240*512))
2147450880

Then write that value into the drive's discard_max_bytes setting. In my case:

# echo 2147450880 > /sys/block/sda/queue/discard_max_bytes

Now, to confirm TRIM is enabled, run:

# fstrim -v /
/: 117.6 MiB (123346944 bytes) trimmed

It should not give an error, and depending on how many blocks it needs to clean up, it could take a few seconds (or longer!).

Making it stick

These values will all be reset next time you reboot the Pi. After a reboot, you get:

# fstrim /
fstrim: /: the discard operation is not supported

So, to make the rules stick, you need to add a udev rule:

# nano /etc/udev/rules.d/10-trim.rules

And add the following in that file:

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

Now, you're probably wondering, _where the heck did he get the idVendor and idProduct? I used the handy lsusb utility:

# lsusb
Bus 002 Device 002: ID 1b1c:1a0e Corsair 
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 002: ID 2109:3431 VIA Labs, Inc. Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

And looking at the 'Corsair' line, the vendor is the first part of the identifier (1b1c), and the product is the second part (1a0e). If you want a ton of detailed information about a USB device, use lsusb -vd 1b1c:1a0e (with the vendor:product specified).

Anyways, make sure to save your 10-trim.rules file, then reboot the Pi. Try running fstrim again, and make sure it works:

$ sudo fstrim -v /
/: 111.4 GiB (119574462464 bytes) trimmed

The first time fstrim is run after a reboot, it will trim all the free space, which is why it gives such a large number. From that point on, the kernel will track changed blocks and trim only that data until the next boot.

Automatic trimming

The last thing you will need to do to make sure the TRIM command is run automatically in the background (so you don't need to run fstrim manually) is to enable the built-in fstrim.timer.

To do that, run the command:

$ sudo systemctl enable fstrim.timer

By default, it will run weekly. Yay, now you have TRIM! That is, if your drive supports it. Check back in later and subscribe to my blog or YouTube channel—I'll be posting a video and blog post with data on which drives support TRIM!

Comments

An update, based on the bold Warning in the middle of this page: Apparently I have corrupted the firmware/controller of my Arcanite USB 3.1 flash drive by trying to enable TRIM on it. If I try formatting it on my Mac with Disk Utility I get Unable to write to the last block of the device. : (-69760). It doesn't boot anymore on the Pi. It won't mount on my Dell laptop, and if I try writing to it with dd, it now writes at like 200 KiB/s (it used to write at 50+ MiB/s). I'll check on dd later to see if it finishes writing a new image after 6 hours :P

Excellent job, Jeff. Thanks a lot for your detailed technical explanation.
I was successful following your steps to enable TRIM on my Odroid XU4 board (16.04.7) for 2 SAMSUNG SSD drive:
1. Samsung SSD 870 EVO 1TB hooked via usb3 SataToSSD adapter StarTech.com USB 3.1 SATA III ( 6 Gbps ) USB312SAT3CB
2. Samsung SSD 860 EVO 500G hooked via usb3 SataToSSD adapter Inateck USB 3.0 SATA III

Excellent and detailed technical writing. Applied TRIM successfully per your steps for my Kingston SA400 SATAIII SSD 2.5 Inch 120 GB (SA400S37) --> hooked via usb3 to rpi4 running Raspbian.

Hi Tuomas, can you tell what adapter you use? I have kingston a400 120gb but after reboot all is reset, I have followed entire procedure.

If it is useful to anyone, I have a Sabrent USB3.0 drive enclosure (EC-UASP) which has the chipset JMS561U (I believe this drive can have different chipsets). Looking up this chipset it states that TRIM is not supported, however Sabrent had an update on their website for the firmware. Thought I would run the risk and try this. Well it worked! Now I have TRIM running successfully! Great article!

Thank you for writing this article! Going to script this out tonight.

Ooh, I thought about doing that but just haven't had the time. If you can throw something up on gist.github.com I'll add a link to it in the post!

And what about rpi3 B/B+ ?

Can I use that procedure to enable trim on my internal SSD connected on USB with a sata to usb adapter ? (using some ASMedia chipset ASM1051E/ASM1053E/ASM1153E)

as far as I can tell, no :( sorry to say

$ lsusb
...
Bus 002 Device 003: ID 174c:55aa ASMedia Technology Inc. Name: ASM1051E SATA 6Gb/s bridge, ASM1053E SATA 6Gb/s bridge, ASM1153 SATA 3Gb/s bridge, ASM1153E SATA 6Gb/s bridge
...
$ sg_vpd -p bl /dev/sdb
Block limits VPD page (SBC):
...
  Maximum unmap LBA count: 0 [Unmap command not implemented]
...

Well, it depends on the drive connected. I use the the same adapter with a cheap Sandisk Ultra 3D and I get

# sg_vpd -p bl /dev/sda
...
Maximum unmap LBA count: 4194240
...

I managed to get it to work on my SATA to USB 3 bridge from Inateck (UA1002) which is driven by a ASM1153e after a firmware update. You need to dl Asmedia 105x MPTool (for Windows) and the 140509_a1_82_40.bin firmware. You can easily google both.
The update tool indicated:FAIL but the firmware got flashed and it works.
I followed the instructions and it was easy to do! A big thank you to the author!

I bought the USB SATA bridge this febuary 2021 and the firmware was starting with 13xxxxxxxx!

It depends on the adapter. Mine is a StarTech USB312SAT3CB which states ID 174c:55aa ASMedia Technology Inc. Name: ASM1051E SATA 6Gb/s bridge, ASM1053E SATA 6Gb/s bridge, ASM1153 SATA 3Gb/s bridge, ASM1153E SATA 6Gb/s bridge.
After applying a firmware update provided by the manufacturer I was able to activate TRIM.

I can confirm it worked on mine. TRIM was succesfully after rebooting enabled :)

idVendor 0x174c ASMedia Technology Inc.
idProduct 0x55aa ASM1051E SATA 6Gb/s bridge, ASM1053E SATA 6Gb/s bridge, ASM1153 SATA 3Gb/s bridge, ASM1153E SATA 6Gb/s bridge
bcdDevice 1.00
iManufacturer 2 Argon
iProduct 3 Forty

Just a small addition to this fantastic article.

I assume it does always trim all the free space when fstrim is called manually. At least for me the output corresponds to the free space according to df.

I recommend enabling online-discard. This enabled the filesystem to discard blocks on the go instead of once a week. For ext4 just add the option "discard" to /etc/fstab.

Couldn't find the details for the msata drive attached using a usb hat thing with lsusb. This worked:

 hwinfo --disk

Thanks for all your hard work.

Hi Jeff thanks for sharing! I'm stuck on this step, trying to update the discard_max_bytes value for my drive:

root@ubuntu:/home/ubuntu# echo 10737418240 > /sys/block/sda/queue/discard_max_bytes
bash: echo: error de escritura: Argumento inválido

I give a write error: not valid argument... any idea what I'm doing wrong?

Same error: Invalid argument with my 1TB Samsung USB drive. echo 68719476736 > /sys/block/sda/queue/discard_max_bytes (block size is 512 and the Maximum unmap LBA count is 134217728

The order of OS modifications is important. The (Gnu/)Linux people are smart. Some appropriate file with absolute name '/sys/devices/.../provisioning_mode' must have the content 'unmap\n' (rather than 'full\n'). The location can change each time the external SSD is mounted, so don't count on the exact path to be stable. Use the find command to find the find file (or files?) named 'provisioning_mode'. After that change, the file /sys/block/sda/queue/discard_max_bytes can be changed to a byte count appropriate for unmap.

The error is because the value you're trying to input (the product of the maximum unmap LBA value and the block size) exceeds 2^32. Your value of 10737418240 looks like that for a 2Tb drive. If you just leave it at the default value the drive will trim OK. I have 2Tb and 8Tb SSDs that trim OK without updating the discard_max_bytes value.

The reason you get 111.4GB after a reboot is because that's the amount of free space and the kernel doesn't know if those sectors are dirty or have ever been trimmed - and there's no way to keep that on disk or in the filesystem. It'll just trim everything to start. Once that's done, the kernel can start to keep track of which sectors have been written and freed, and are therefore eligible for trimming... at least as long as the device remains connected.

Great blog. Even as a relative newbie i was able to get trim enabled on my Crucial MX500 wtith Eluteng adapter.
Thanks for sharing your knowledge.

From what I can see, nothing in the instructions here is preserving the value for discard_max_bytes across a reboot.

Probably not the best idea, but i have added the appropriate line of code into /etc/rc.local

lol , you have the nerve assuming that i am not a Newbie :D I am probably 3 months late, but ...
I entered the equivalent line which comes out to this line for you from the guide "echo 2147450880 > /sys/block/sda/queue/discard_max_bytes"

Usually there is no need to set it.
From https://www.kernel.org/doc/html/latest/block/queue-sysfs.html

discard_max_hw_bytes (RO):
"The discard_max_bytes parameter is set by the device driver to the maximum number of bytes that can be discarded in a single operation."

discard_max_bytes (RW):
"While discard_max_hw_bytes is the hardware limit for the device, this setting is the software limit. Some devices exhibit large latencies when large discards are issued, setting this value lower will make Linux issue smaller discards and potentially help reduce latencies induced by large discard operations."

Hi Jeff,

In your article about booting from SSD, you indicated that the TDBT enclosure with an SX6000 SSD had the ability to do TRIM, but it was not out of the box.

I have recently bought the same enclosure and drive, but my Maximum unmap LBA count says -1 [unbounded].

Did you manager to get TRIM working on your TDBT+SX6000?
Does -1 mean I cannot enable TRIM?
If I can, how do I do the math for discard_max_bytes?

Anyway, any assistance/feedback would be much appreciated. :) Thanks!
Krys

Hi Jeff, after following your guide at 100%, after reboot raspi they reset unmap to full and i get fstrim: /: the discard operation is not supported message.
I use classic startech usb 3.0 adapter and a kingston A400 120GB, in lsusb I have take vendor and product but the code is related to VIA device, I don't see kingston, is this the problem?

Thanks for this excellent article.
However, I need some help getting `idVendor` and `idProduct` of my SSD which is connected via a powered USB hub. When I run `lsusb` all I get is the IDs of the hub itself. How can I get the IDs of the SSD?
Thanks for your help.

Alright, I figured it out myself and now everything is working as it should. Reading the linked comment and whole post in the raspberrypi forum helped a lot.

I activate trim support for my kingstan a400 ssd. thank you for this blog.

I used your instructions to enable trim on my RPI4 and Sandisk 240GB SSD using an ASM1153E based USB to SATA adapter. Thanks

Thanks for the information, however you basically just reported what already written in the following blog, I think you should have mentioned it:
https://lemariva.com/blog/2020/08/raspberry-pi-4-ssd-booting-enabled-tr…
Please notice that you do not need to manually update the "discard_max_bytes" setting, as it is automatically updated once you set the "provisioning_mode" to "unmap". You can double check its value with "cat /sys/block/sda/queue/discard_max_bytes".

Hi,

Hmm... funny ICY BOX M.2 NVMe Case with USB 3.1 Gen2
IB-1824ML-C31

seems not supporting trim - or do I interprete the output wrong?

sg_vpd -p bl /dev/sdb
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: 8 blocks
Maximum transfer length: 65535 blocks
Optimal transfer length: 65535 blocks
Maximum prefetch transfer length: 65535 blocks
Maximum unmap LBA count: -1 [unbounded]
Maximum unmap block descriptor count: 63
Optimal unmap granularity: 0 blocks [not reported]
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]
root@ryzen-5-1600AF:/home/ug/Downloads# sg_vpd -p lbpv /dev/sdb
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]

Thought a nvme adapter should work out of the box with trim, but no...... RGB LEDs are more important than trim?

Cheers
4920441

Does the USB enclosure I'm using affect whether or not it will say trim is supported?
I have a Crucial MX 500 in a Sabrent case that I have to use "quirks" to get it to work, so it doesn't use UASP so I'm probably going to try something else when I can. Crucial says their drive do automatic trim that is sufficient for most casual users.

Great post! A couple of changes that I had to make...

1) As mentioned in other comments, the change to discard_max_bytes is not preserved between reboots. This resulted in the following error for me:
fstrim: /: FITRIM ioctl failed: Remote I/O error usb

To resolve this, I added the following line to /etc/udev/rules.d/10-trim.rules (I managed to get this together thanks to various hints across the internet):
KERNEL=="sda", SUBSYSTEM=="block", ATTR{queue/discard_max_bytes}="(insert the value here)"

Now, trim should work after a reboot.

In addition, I could not enable fstrim.timer, with the following error:
Failed to enable unit: File fstrim.timer: No such file or directory

In order to "install" the files (thanks to the post http://forums.debian.net/viewtopic.php?f=10&t=140417), I ran this command:
sudo cp /usr/share/doc/util-linux/examples/fstrim.{service,timer} /etc/systemd/system

And then I was able to enable the service.

It's important to note that using the command in this post (sudo systemctl enable fstrim.timer) will only activate this service on the next reboot. As such, you should either reboot after, or add --now after the enable (thanks again to various posts across the internet).

Hope this helps other searchers!

Hello,
I've just completed the tutorial. After reboot, I receiven an "fstrim: /: FITRIM ioctl failed: Remote I/O error usb" error.
Then I have updated the rules file with values given in your comment.
Then I vave the "fstrim: /: the discard operation is not supported" error.

Could You help me with it ?

BR,
Jakub

Thanks for this great HOWTO! After pulling a 250GB Samsung 850 EVO SSD out of an old laptop to use with my new Pi 400 and using SD Card Copier to clone the SD card to the SSD (using a UGREEN AS1153E-based SATA-to-USB3.0 enclosure, ID 174c:55aa) I shut down, popped out the SD card and rebooted from SSD (excellent!) then followed your instructions and everything worked out great without any problems!

Thanks again for your work figuring this stuff out!

Thank you Jeff, the fact that your walkthrough was based on the Corsair Voyager made this extremely easy for me.

Thanks it worked great, ten minutes after finding this and it was working. After days of trying to find the correct way to do it, as with everything there is a lot of garbage information out there....

Hello Jeff,

first of all please let me thank you so much for your blog! I got many hints for getting out the maximum of my RasPi! :-)

So I'm able to boot my Raspberry Pi 4B with an external Icy Box IB-1922MF-C32 Enclosure (ASM2364 chipset) and a Western Digital WD Blue SN550 NVMe SSD in it without any problems! UASP Support works out of the box also.

Then I dared to get TRIM Support working. First I've checked if my combination will support TRIM and this was confirmed by all commands. Then I made it stick with an udev rule and could run fstrim -v / after a reboot. So far so good.

The only thing I struggled with was the discard_max_bytes setting. LBA count says 134217728 and multiplied by 512 (logical block length) results in 68719476736. Echoing this into /sys/block/sda/queue/discard_max_bytes ended up with the invalid argument error equal to some comments earlier. cat /sys/block/sda/queue/discard_max_bytes says 4294966784.

Can you please explain this behavior to me?

After all it looks good and TRIM is triggered once a week by systemd. As an addition you could add sudo systemctl start fstrim.timer to your tutorial, because sudo systemctl enable fstrim.timer just enables it, but doesn't start it. You can check it's status with sudo systemctl status fstrim.timer.

Have a nice day and stay healthy!

Great tutorial,
I was using already using a WD Green 480Gb SSD for booting up my Pi4 and this article has been enormously helpful.
Thank you so much

Thanks for the tutorial Jeff! Followed the steps and it worked perfectly. Have a good one!

Excellent Tutorial !!!! Thanks !
Used on raspberry 4 b+ , manjaro arm ,with wd blue ssd nand and ID 174c:235c ASMedia Technology Inc. Ugreen Storage Device usb-c

Works great (Samsung Portable SSD T5)! Thanks for the tutorial!

Hi Jeff

Thanks for this excellent post.

It looks like a recent update of PiOS may have broken something as the TRIM support is lost on reboot. I works fine after I re-issue the two echo commands as sudo. I can confirm it used to work flawlessly until I updated PiOS a couple of days back.

It'll be great if you or anyone else on here could confirm.

Thanks!

Worked for me with UGREEN 2,5" to USB-C 3.1 and 870 EVO like a charm. Thanks!

Quick update from me (super thanks for the article, Jeff). I have a raspberry pi 4, booting off an enclosed ssd via usb, but I also have a usb 3.0 hub with 4 other SSDs drives (with usb enclosures) too.

tl;dr, I only got things working by putting this in root's crontab for each drive:

# i can't type the at sign here due to restrictions in comment content
`[at sign]reboot echo unmap > /sys/devices/<.....>/provisioning_mode`
`[at sign]reboot echo 2147450880 > /sys/block/sda/queue/discard_max_bytes`

Longer version:
I found I just couldn't get idVendor or idProduct from the attached drives when attached to a hub (and didn't want to plug/unplug with dmesg), so tried putting commands in rc.local. However, that didn't work as the usb hub was still spinning up attached drives whilst rc.local runs, so the paths don't exist at that point. Only by putting the above entry in the root's crontab (i.e. sudo cronab -e) at reboot could I get this working.
Note: I found, during set up, that in the "echo unmap > ..." line, if I had a bad connection on the first drive and the hub didn't recognise it, the unmap path would change for all the other hub drives! So the above works, but getting idVendor or idProduct (assuming they're unique over a hub anyway) would be the optimum solution.

Hi, thank you so much for this guide! It worked flawlessly and also helped me understand what's going on in the background. One question:
If I set up TRIM again on a fresh install, using the same pi, the same adapter, and the same ssd, am I correct in assuming the steps are as following? I don't want to accidentally brick my setup. I'm mainly concerned about the first step, as I am not sure what will be reset upon reboot, and what will stick, but I'd assume that the carefully calculated discard_max_bytes value has to be used somewhere:

1. write the calculated value into /sys/block/sda/queue/discard_max_bytes

2. add udev rule that changes provisioning_mode to unmap

3. enable the timer

Thank you so much for your help!
Best,
Bin

Really nice, thank you !
Can u please add an example line in your Guide for:
ExecStart=/usr/bin/python3 /usr/local/bin/trim.py ENTER_DEVICE_HERE

Some people can get slightly irritated, what you exactly want.
But so far, thank you very much !

It depends on the firmware of your ASMEDIA chip. Some support trim/uasp some do not.

To find out you need to reproduce the steps at least one time and after that you usually just create
/etc/udev/rules.d/10-trim.rules

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

After a reboot trim should work (if supported) and you'll just need to start the service/cron.
Seems somehow easier to me ;).

This did the trick on my flash drive that wasn't writing as fast as it was supposed to anymore! Thanks a ton.

Thank you very much, it's quite incredible that it's not supported by default, even for devices that are known to be the best compatible with linux (in my case the USB312SAT3CB)!

Thank you for posting this article, most people are unlikely to see a performance hit on an SSD without TRIM enabled having being used to SD cards but it is beneficial to the reliability of the drive for it to be working and like many others I had assumed that it was. I would hope that Pi OS will feature TRIM out of the box at some point.

Hi Jeff, thanks a lot for the very clear instructions. It works like a breeze. I'm new to the Raspberry Pi 400, but articles likes yours really help a lot to discover how to make my system better.
I'm using a Startech USB3S2SAT3CB interface with a Kingston A400 240 Gb SSD and all your instructions worked first time around. So now I have automatic TRIMming of my SSD on my Raspberry. How cool is that. Thanks again!

I am using a UGreen USB enclosure (with an ASMedia chipset ASM1051E/ASM1053E/ASM1153E) on OMV5 on RPi4

I get to the point in the guide to enable TRIM but get a permission denied message:

pi@ripple:~ $ sudo echo 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

-bash: /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: Permission denied

Any help would be great as I'm really new to using Linux.

Hi,

I managed to solve the permission error. I was trying to use the command not as root.

sudo su then repeating the command worked and so did the rest of the guide.

Thank you Jeff for this excellent guide! I now have TRIM working on my RPi4 :)

I think you have to switch to the root user: sudo su before you can make these changes. No expert on Linux myself though, but that’s what I’ve learned by now :-)

hi kind sirs, i have a StarTech USB3S2SATA3CB with ASMedia 105x and im not able to get TRIMM to stick.. I follewed the guide up to "echo 2147450880 > /sys/block/sda/queue/discard_max_bytes" where i get an error about bad argument. i contacted StarTech but they said TRIMM should be enabled by default and they dont provide firmware for this adaptor.im using a Crucial MX500 1TB SSD which supports TRIMM. any help would be great, thank you

When I ran the sudo fstrim -v / command I got:
/: 386.9 MiB (405708800 bytes) trimmed
But when I then run sg_vpd -p bl /dev/sda
I got
Block limits VPD page (SBC):
...
Maximum unmap LBA count: 0 [Unmap command not implemented]
Maximum unmap block descriptor count: 0 [Unmap command not implemented]

So, it would appear that even though TRIM is enabled the sg_vpd command is not showing the expect non-zero values for the above?

Thank you
just successfully enabled TRIM on Seagate Expansion slot (from external HDD) + Crucial BX500 1tb SSD
Worked fine

I have many WD Elements drives plugged in into the Raspberry PI via the hub.
One of these WD Elements is my SSD drive (I removed the original hdd from the enclosure and placed the SSD instead).
When I modified the /etc/udev/rules.d/10-trim.rules - all of my WD drives were reporting provisioning mode unmap - because for all those drives the parameters idVendor and idProduct were the same.
Of course, trying to fstrim on them caused an error (if it did not, that would probably mean I have one of those infamous SMR drives, as they support TRIM as well)

In order to pick only one particular device, my syntax of the 10-trim.rules was:
ACTION=="add|change", ATTRS{idVendor}=="1058", ATTRS{idProduct}=="25a3", KERNELS=="2-1", SUBSYSTEM=="scsi_disk", ATTR{provisioning_mode}="unmap"

Where did I find this KERNELS ?
By running the following command on my disk:

udevadm info -ap /sys/devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb2/2-1/2-1:1.0/host0/target0:0:0/0:0:0:0/scsi_disk/0:0:0:0

Among other parameters I found:
KERNELS=="2-1"
ATTRS{devnum}=="2"
ATTRS{devpath}=="1"
ATTRS{idProduct}=="25a3"
ATTRS{idVendor}=="1058"

I decided to use KERNELS=="2-1", but I could also use ATTRS{devnum}=="2", ATTRS{devpath}=="1" instead - as these identify my device unambiguously.

Additional note. I am using Patriot Blast 480GB SSD with the WD Elements enclosure.
However, the command
sg_vpd -p bl /dev/sda
returns
Maximum unmap LBA count: -1 [unbounded]

The value /sys/block/sda/queue/discard_max_bytes shows 4294966784 (which, I believe, is 4GB)

A quick test shows that trim works though.

me@moo:/# df -h | grep sda2; dd if=/dev/zero bs=1M of=test.txt oflag=sync status=progress; rm test.txt; sync; fstrim -v /
/dev/sda2 118G 83G 30G 74% /
36803969024 bytes (37 GB, 34 GiB) copied, 477 s, 77.1 MB/s
dd: error writing 'test.txt': No space left on device
35106+0 records in
35105+0 records out
36810944512 bytes (37 GB, 34 GiB) copied, 477.319 s, 77.1 MB/s
/: 34.3 GiB (36818255872 bytes) trimmed

Thanks so much for this, worked like a charm on Argon one M.2 case with a Samsung 860 EVO.

Big thanks Jeff for the detailed documentation, that solved my problem exactly as you described it here!
As help for others: I run an Ubuntu 20.04 LTS on a RasPi4 via a USB2SATA controller (usb3s2sat3cb: ID 174c:55aa ASMedia Technology Inc. Name: ASM1051E SATA 6Gb/s bridge, ASM1053E SATA 6Gb/s bridge, ASM1153 SATA 3Gb/s bridge, ASM1153E SATA 6Gb/s bridge; with a transcend SSD230S). But contrary to other links said that there is a firmware to run to enable Trimming it - that's not true, I chatted with the support and they say there is no firmware update for that device

Thanks Jeff for your great instructions that work just fine.

I need the idVendor and idProduct to enable the TRIM of the SSD but when I type the command “lsusb”, I get:
QUOTE
Bus 002 Device 002: ID 174c:1153 ASMedia Technology Inc. ASM1153 SATA 3Gb/s bridge
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 002: ID 2109:3431 VIA Labs, Inc. Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
UNQUOTE
and there is no indication of the installed SSD.

How can I get these idVendor and idProduct? Please advise.
Thanks,
Dan

This is a great post. Only needed to do it twice (as you did probably as well) because after first reboot it was gone! great manual. Thanks.

Help. I have a 2tb Seagate Firecuda 520 NVME and a UGREEN m.2 SATA/NVME enclosure, both of which should support TRIM. When I execute (sg_vpd -p bl /dev/sdb) I get a warning >>> struct opts_t differs in size from sg_vpd.c [52 != 56]. My max unmap LBA count is 20971520. The calculated discard_max_bytes is 10737418240 (based on logical block length of 512). However when I try to set that value, I get an error message "write error: Invalid argument". I attempted to set it with a lower value (the one you used) and it accepted it.

What does the warning struct opts_t differs in size from sg_vpd.c [52 != 56] mean and should I be concerned?
What am I missing, why cant i set the discard_max_value appropriately?

Implemented it for Samsung T5 ... works perfect. Thanks!!

Great post!

I just got an Argon ONE M.2 case and TRIM wasn't working. I believe more recent units have a newer chipset, so that means a different product ID.

Here are my udev rules I have confirmed are working with a Kingston A400:

ACTION=="add|change", ATTRS{idVendor}=="174c", ATTRS{idProduct}=="1156", SUBSYSTEM=="scsi_disk", ATTR{provisioning_mode}="unmap"
ACTION=="add|change", ATTRS{idVendor}=="174c", ATTRS{idProduct}=="1156", SUBSYSTEM=="block", ATTR{queue/discard_max_bytes}="2147450880", RUN+="/bin/sh -c 'cat /sys/block/sda/queue/discard_max_bytes'"

Yes; Great post Jeff! Thanks

And thanks Ben for your udev rules!

I also have the Argon ONE M.2 case with these vendor and product ids,
(it uses the Asmedia ASM225CM USB SATA Controller).

I had figured I needed the first provisioning_mode/unmap entry but your
second discard_max_bytes line is elegant (and working!); something that had
been causing me disquiet...

Now TRIM is working here at last (also) with a Kingston a Kingston SA400M8/240G
M.2 SATA SSD.