{"id":21306126,"url":"https://github.com/brlin-tw/trim-over-usb-howto","last_synced_at":"2026-05-18T22:04:50.132Z","repository":{"id":252776448,"uuid":"841377916","full_name":"brlin-tw/trim-over-usb-howto","owner":"brlin-tw","description":"Research the usability of the unused blocks notification operation against flash storage controllers over the USB interface.","archived":false,"fork":false,"pushed_at":"2024-08-27T15:27:29.000Z","size":287,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-22T08:45:44.078Z","etag":null,"topics":["deallocate","discard","flash-memory","flash-storage","linux","trim","unmap","usb"],"latest_commit_sha":null,"homepage":"https://gitlab.com/brlin/trim-over-usb-howto","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/brlin-tw.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSES/CC-BY-SA-4.0.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-08-12T09:36:09.000Z","updated_at":"2024-08-27T15:27:33.000Z","dependencies_parsed_at":"2024-08-27T17:08:24.975Z","dependency_job_id":null,"html_url":"https://github.com/brlin-tw/trim-over-usb-howto","commit_stats":null,"previous_names":["brlin-tw/trim-over-usb-howto"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brlin-tw%2Ftrim-over-usb-howto","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brlin-tw%2Ftrim-over-usb-howto/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brlin-tw%2Ftrim-over-usb-howto/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brlin-tw%2Ftrim-over-usb-howto/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/brlin-tw","download_url":"https://codeload.github.com/brlin-tw/trim-over-usb-howto/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243784100,"owners_count":20347409,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["deallocate","discard","flash-memory","flash-storage","linux","trim","unmap","usb"],"created_at":"2024-11-21T16:21:41.728Z","updated_at":"2026-05-18T22:04:50.042Z","avatar_url":"https://github.com/brlin-tw.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# How to notify unused blocks to the flash storage controller over the USB interface\n\nResearch the usability of the unused blocks notification operation against flash storage controllers over the USB interface.\n\n\u003chttps://gitlab.com/brlin/trim-over-usb-howto\u003e  \n[![The GitLab CI pipeline status badge of the project's `main` branch](https://gitlab.com/brlin/trim-over-usb-howto/badges/main/pipeline.svg?ignore_skipped=true \"Click here to check out the comprehensive status of the GitLab CI pipelines\")](https://gitlab.com/brlin/trim-over-usb-howto/-/pipelines) [![GitHub Actions workflow status badge](https://github.com/brlin-tw/trim-over-usb-howto/actions/workflows/check-potential-problems.yml/badge.svg \"GitHub Actions workflow status\")](https://github.com/brlin-tw/trim-over-usb-howto/actions/workflows/check-potential-problems.yml) [![pre-commit enabled badge](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit\u0026logoColor=white \"This project uses pre-commit to check potential problems\")](https://pre-commit.com/) [![REUSE Specification compliance badge](https://api.reuse.software/badge/gitlab.com/brlin/trim-over-usb-howto \"This project complies to the REUSE specification to decrease software licensing costs\")](https://api.reuse.software/info/gitlab.com/brlin/trim-over-usb-howto)\n\n## Table of contents\n\n[TOC]\n\n## Problem\n\nA few important characteristics of flash memory storage devices is that:\n\n* Once a memory cell has data written on it(bit flips from 0→1), it can only be rewritten after it is erased(1→0).\n* Memory cells can only be erased in batch(called erase segments), erase segments are usually bigger than memory blocks(e.g. 4MiB).\n* When the count of erased memory cells are low the memory controller will need to do the erase on-the-fly, which results in a very poor write performance.\n\nSome flash storage drives supports notifying unused memory blocks to the controller from the Operating System to do garbage collection(GC) using the following protocol commands:\n\n* ATA: TRIM\n* NVM Express(NVMe): DEALLOCATE\n* SCSI: UNMAP\n* SD/MMC: ERASE/DISCARD\n\nin order to let the storage controller do the garbage collection in the background to maintain write performance.  However, the operation may no be available if:\n\n* It is not implemented (common in lower-end flash storage drives)\n* There's a translation layer between the storage controller and the operating system, like a USB external hard-drive enclosure/cable that translates the SATA/NVMe/MMC... protocol commands into the counterpart SCSI commands which the USB mass storage/UASP specification uses.\n\n  Another issue is that even if the translation layer support such operation, the Operating System may not utilize it due to the storage drive not complying to the relevant specifications.\n\n## Solution\n\nWe'll need to know:\n\n* How to check whether the storage drive supports such operation.\n* If the storage drive supports such operation but the Operating System didn't enables it due to uncompliance, how to override it.\n* If the storage drive claimed to support such operation, how to check whether it works or not.\n\nThe answer of these questions varies based on the following factors:\n\n* The manufacturer and model of the storage drive/enclosure\n* The firmware version of the storage drive/enclosure\n\nWe'll individually collect related info of each device to avoid bias.\n\n## Research process\n\nThis section documents the process of determining the support:\n\n### Determine the kernel name of the storage device\n\nRun the following commands in a text terminal to determine the kernel name of the storage device:\n\n```bash\nlsblk_opts=(\n    # Don't print sub-device nodes\n    --nodeps\n\n    # Exclude loopback devices(e.g. snaps)\n    --exclude 7\n\n    # Specify output columns that are useful in determining the kernel\n    # name of the storage device block device\n    --output NAME,VENDOR,MODEL,SERIAL,SIZE\n)\nlsblk \"${lsblk_opts[@]}\"\n```\n\n\u003c!--\nAccording to [the command output](lsblk.out.txt) we can determine that the kernel name of the storage device is `_kernel_name_`.\n--\u003e\n\n### Determine whether the unused block notification feature is enabled by the operating system by default\n\nRun the following commands in a text terminal to determine the kernel name of the storage device:\n\n```bash\ndevice_kernel_name=_kernel_name_\ndevice=\"/dev/${device_kernel_name}\"\nlsblk_opts=(\n    # Don't print sub-device nodes\n    --nodeps\n\n    # Print information about the unused block notification capabilities\n    # for each device.\n    --discard\n)\nlsblk \"${lsblk_opts[@]}\" \"${device}\"\n```\n\n\u003c!--\nAccording to [the command output](lsblk-discard-native.out.txt) we can\ndetermine that the operating system didn't enable the unused block\nnotification feature for this storage device.\n--\u003e\n\n### Check whether the storage device has declared implementation of the Logical Block Provisioning Management feature specified by SBC-4\n\nRun the following command _as root_ in a text terminal to query the\nresponse of the SCSI READ CAPACITY (16) command of the storage device:\n\n```bash\ndevice_kernel_name=_kernel_name_\ndevice=\"/dev/${device_kernel_name}\"\nsg_readcap_opts=(\n    # Use the 16 byte cdb variant of the READ CAPACITY command, allow\n    # proper results for storage drives capacity over 2TiB\n    # (2**32 - 2) * 512 / 1024 / 1024 / 1024\n    --long\n)\nsg_readcap \"${sg_readcap_opts[@]}\" \"${device}\"\n```\n\n\u003c!--\nAccording to [the command's output](sg_readcap-long.out.txt) we can\nverify that the storage drive _claims_ that it does not support the\nLogical Block Provisioning Management feature specified by SBC-4,\nwhich _contradicts_ with the support of the UNMAP command in the\nprevious steps:\n\n```txt\nRead Capacity results:\n   Protection: prot_en=0, p_type=0, p_i_exponent=0\n   Logical block provisioning: lbpme=0, lbprz=0\n\n    ...stripped...\n\n```\n--\u003e\n\n### Check the SCSI vital product data(VPD) pages supported by the SCSI device\n\nRun the following command _as root_ in a text terminal to determine which SCSI vital product data(VPD) pages supported by the SCSI device:\n\n```bash\ndevice_kernel_name=_kernel_name_\ndevice=\"/dev/${device_kernel_name}\"\nsg_vpd \"${device}\"\n```\n\n\u003c!--\nAccording to [the command's output](sg_vpd.out.txt) the following VPD pages are available:\n\n* Supported VPD pages \\[sv\\]\n* Unit serial number \\[sn\\]\n* Device identification \\[di\\]\n*\n--\u003e\n\n### Check the supported parameters of the SCSI UNMAP command of the storage device\n\n\u003c!--\nLet's temporarily disregard the fact that the storage device have\nclaimed it do not have Logical Block Provisioning Management and\nassume that it _do_ in fact support the UNMAP SCSI command, let's check\nwhether there's any limitations that needs to take note of during the\nusage of UNMAP SCSI commands.\n--\u003e\n\nRun the following command _as root_ in a text terminal to query the\nBlock limits VPD page of the storage device:\n\n```bash\ndevice_kernel_name=_kernel_name_\ndevice=\"/dev/${device_kernel_name}\"\nsg_vpd_opts=(\n    --page=bl\n)\nsg_vpd \"${sg_vpd_opts[@]}\" \"${device}\"\n```\n\n\u003c!--\nAccording to [the command's output](sg_vpd-bl.out.txt) we can verify\nthat the storage drive _claim_ it can notify _block_quantity_ unused logical\nblocks in a single SCSI UNMAP command:\n\n```txt\nBlock limits VPD page (SBC):\n\n    ...stripped...\n\n  Maximum unmap LBA count: _block_quantity_\n```\n--\u003e\n\n\u003c!--\nWhat is the size of a logical block anyway, let's check [the previous\noutput of the `sg_readcap --long` command](sg_readcap-long.out.txt) for\nthat:\n\n```text\nRead Capacity results:\n\n    ...stripped...\n\n   Logical block length=512 bytes\n```\n\nSo the size of a logical block is 512 bytes, after the following\ncalculation and unit conversion we can conclude that the storage\ndevice can accept _batch_size_ bytes(_batch_size_human_readable_ to be exact)\nof unused blocks notification in a single UNMAP SCSI command:\n\n$$\n\\begin{align}\nNotifiable\\ unused\\ memory\\ size\\ per\\ UNMAP\\ command \u0026= _block_quantity_\\ blocks \\times 512\\ B/block \\div 1024\\ KiB/B \\div 1024\\ MiB/KiB \\div 1024\\ GiB/MiB \\\\\n\u0026\\approx _batch_size_human_readable_ GiB\n\\end{align}\n$$\n\nAt least in the assumption that the storage device did announce it\ncorrectly(which we already know, it didn't in some places).\n--\u003e\n\n### Enable unused block notification support by force\n\nAlthough the missing declaration of the SCSI Logical Block Provisioning\nManagement feature, there's still possibility that the drive actually\nimplemented the unused block notification functionality in the firmware.\nLet's try to find out.\n\n**Warning:** There's a reason why the functionality is not enabled by\ndefault as there's a chance that the controller have a problomatic\nreaction when facing the UNMAP SCSI command, which may results in\nproblems including but not limited to:\n\n* The drive simply fails and no longer functions properly, rendering\n  it no longer usable for data storage/access.\n* The drive erratically respond to the command and erases memory blocks\n  that are not requested, leads to data loss.\n\n**Only continue if you can take the responsibility of device\nfailure/data recovery.**\n\nIn order to force enable the unused block notification feature, we need\nto first determine the address of the SCSI device of the storage device\n, which can be queried by running the following command in a text\nterminal:\n\n```bash\nlsscsi\n```\n\n\u003c!--\nFrom [the command's output](lsscsi.out.txt) we can determine that the\naddress of the SCSI device is `0:0:0:0`.\n--\u003e\n\nWe can force enable the unused block notification support feature using\nthe UNMAP SCSI command by running the following commands _as root_ after\nsetting the proper `scsi_device_address`:\n\n```bash\nscsi_device_address=_address_\necho unmap \u003e \"/sys/class/scsi_disk/${scsi_device_address}/provisioning_mode\"\n```\n\nNow that the UNMAP command is enabled, run the following commands to\ncheck whether the data size of each UNMAP SCSI command set by the kernel\nis sane:\n\n```bash\ndevice_kernel_name=_kernel_name_\ncat \"/sys/block/${device_kernel_name}/queue/discard_max_bytes\"\n```\n\n\u003c!--\nFrom [the command's output](sysfs-block-queue-discard_max_bytes-after-overriding-provision_mode.out.txt)\nyou can notice that the system detected that you can notify 4,294,966,784\nbytes(=4GiB - 512bytes) of unused data in a single UNMAP SCSI command,\n**which contradicts with the _batch_size_ bytes limitation we\npreviously determined**.\n\nLet's set it to the proper value by running the following command _as\nroot_:\n\n```bash\ndevice_kernel_name=_kernel_name_\necho _batch_size_ \u003e \"/sys/block/${device_kernel_name}/queue/discard_max_bytes\"\n```\n--\u003e\n\n### Test whether unused block notification actually work\n\nNow we can test whether the unused block notification really work by\ntriggering a whole drive/partition block device discard operation by\nrunning the following commands _as root_:\n\n```bash\ndevice_kernel_name=_kernel_name_\ndevice=\"/dev/${device_kernel_name}\"\nblkdiscard_opts=(\n    # Disable safeguard checks\n    --force\n\n    # Print details of the operation\n    --verbose\n)\nblkdiscard \"${blkdiscard_opts[@]}\" \"${device}\"\n```\n\n\u003c!--\nYou should see the following output, indicate that the discard\noperation is a success:\n\n```txt\nblkdiscard: Operation forced, data will be lost!\n/dev/_kernel_name_: Discarded _storage_size_ bytes from the offset 0\n```\n\nHowever, if you check the binary content of the storage drive you'll\nfind that the drive's content isn't erased at all:\n\n```txt\ndevice_kernel_name=_kernel_name_\ndevice=\"/dev/${device_kernel_name}\"\n$ sudo xxd -l 128 \"${device}\"\n00000000: eb63 9000 0000 0000 0000 0000 0000 0000  .c..............\n00000010: 0000 0000 0000 0000 0000 0000 0000 0000  ................\n00000020: 0000 0000 0000 0000 0000 0000 0000 0000  ................\n00000030: 0000 0000 0000 0000 0000 0000 0000 0000  ................\n00000040: 0000 0000 0000 0000 0000 0000 0000 0000  ................\n00000050: 0000 0000 0000 0000 0000 0080 0100 0000  ................\n00000060: 0000 0000 fffa 9090 f6c2 8074 05f6 c270  ...........t...p\n00000070: 7402 b280 ea79 7c00 0031 c08e d88e d0bc  t....y|..1......\n```\n\nTherefore the unused block notification does not work at all in this\nstorage device, even when the device has claimed support for the\nUNMAP SCSI command.\n--\u003e\n\n## Research results\n\nWe currently have research results for the following products:\n\n* [伽利略 M2NVU31 M.2(NVMe) PCI-E SSD to USB3.1 Gen2](\u003c伽利略 M2NVU31 M.2(NVMe) PCI-E SSD to USB3.1 Gen2\u003e)\n* [OWC USB-C Travel Dock E(SD card reader)](\u003cOWC USB-C Travel Dock E(SD card reader)\u003e)\n* [Transcend JetFlash 790 Series 64GB](\u003cTranscend JetFlash 790 Series 64GB\u003e)\n\n## References\n\nThe following material are referenced during the development of this project:\n\n* [Discard over USB - Gentoo Wiki](https://wiki.gentoo.org/wiki/Discard_over_USB)  \n  Explains how to enable discard/trim operation for block devices via the USB bus.\n* [External SSD with TRIM support - Solid state drive - ArchWiki](https://wiki.archlinux.org/title/Solid_state_drive#External_SSD_with_TRIM_support)  \n  Explains how to check and enable TRIM operation on solid storage drives over a USB-to-SATA bridge chips.\n* [Hardware support - Trim (computing) - Wikipedia](https://en.wikipedia.org/wiki/Trim_(computing)#Hardware_support)  \n  Explains the equivalent commands of unused block notification operation for each protocols.\n* [sd: disable logical block provisioning if 'lbpme' is not set - Patchwork](https://patchwork.kernel.org/project/linux-scsi/patch/20190214221558.09174756@endymion/)  \n  Explains the reasoning that Linux Kernel requires the LBPME bit in order to enable unused memory blocks notification support.\n* [SCSI Commands Reference Manual 100293068, Rev. J](https://www.seagate.com/files/staticfiles/support/docs/manual/Interface%20manuals/100293068j.pdf)  \n  Documents the Serial Attached SCSI(SAS) commands in the SCSI specifications, sections that may include useful information are:\n    + 5.4 Vital product data parameters\n* [Enabling TRIM on an external SSD on a Raspberry Pi | Jeff Geerling](https://www.jeffgeerling.com/blog/2020/enabling-trim-on-external-ssd-on-raspberry-pi)\n* [Trying to get SSD boot working on pi4 - Page 2 - Raspberry Pi Forums](https://www.raspberrypi.org/forums/viewtopic.php?p=1708655#p1708655)\n* The lsblk(8) manual page  \n  Explains how to use the `--nodeps` and `--output` command-line options.\n* The sg_vpd(8) manual page  \n  Explains how to use the `--long` `sg_vpd` command-line option.\n* [‎Gemini - LBP in SCSI](https://gemini.google.com/share/e90021aaed7c)  \n  Introduces what Logical Block Provisioning Management (LBP) in SCSI do.\n* [SCSI device location codes - IBM Documentation](https://www.ibm.com/docs/en/aix/7.3?topic=codes-scsi-device-location)  \n  Explains the format of the SCSI device location code.\n* [SCSI Addressing](https://tldp.org/HOWTO/SCSI-2.4-HOWTO/scsiaddr.html)  \n  Explains the format of the SCSI device addresses.\n* [Queue sysfs files | Info on the Block I/O (BIO) layer | Linux kernel plaintext documentation](https://www.kernel.org/doc/Documentation/block/queue-sysfs.txt)  \n  Explains the definition of the discard_max_bytes sysfs file.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrlin-tw%2Ftrim-over-usb-howto","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbrlin-tw%2Ftrim-over-usb-howto","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrlin-tw%2Ftrim-over-usb-howto/lists"}