TRIM/UNMAP w dyskach SSD podłączonych via adapter USB-SATA na linux
Spis treści
Jakiś czas temu opisywałem jak na Debianie włączyć obsługę mechanizmu TRIM (realizowanego
przez polecenie fstrim
) na podpiętych do komputera dyskach SSD. Problem w tym, że dyski SSD, z
którymi będziemy wchodzić w interakcję, nie zawsze będą podłączane do dedykowanych portów
SATA/mSATA. Jak zachowa się zatem nasz linux, gdy będziemy chcieli podłączyć po portu USB
zewnętrzny dysk SSD? Przez zewnętrzny dysk USB nie mam na myśli dedykowanych zewnętrznych dysków
USB, bo te raczej nie powinny sprawiać kłopotów. Chodzi mi bardziej o wewnętrzne dyski SSD (np. ze
starego laptopa), które zamkniemy w dedykowanej obudowie USB, lub też będziemy taki dysk podłączać
na krótko za pomocą adaptera USB-SATA. W takich przypadkach zwykle kernel linux'a nie odważy się
włączyć wsparcia dla TRIM dla nośników SSD i tak właśnie się stało w przypadku mojego nowo
zakupionego dysku od Goodram, a konkretnie jest to model SSDPR-CX400-02T-G2
, który to został
podłączony do portu USB3 mojego Raspberry PI (i Debiana) przy pomocy kabelka USB-SATA (Unitek
USB3.1 USB-A to 2.5" SATA6G). Przez kilka miesięcy dysk sprawował się bez zarzutu ale ostatnio przy
próbie wgrania na niego danych (przez sieć), transfer spadł do dosłownie pojedynczych MiB/s.
Poszukałem trochę informacji i okazało się, że dla tego typu nośników trzeba ręcznie włączyć
TRIM, o ile będzie to w ogóle możliwe.
Czy mój dysk SSD na USB wspiera TRIM
Ten nowo zakupiony dysk SSD ma w zasadzie działać w roli storage pod filmy/seriale odtwarzane na TV podpiętym do mojego Raspberry Pi 4B. Dlaczego dysk SSD (i to jeszcze w technologi SLC), a nie dysk HDD? Bo dyski SSD nie mają głowic magnetycznych i przy takim zastosowaniu (w roli dysku pod TV), nawet dysk SSD SLC będzie o wiele żywotniejszy niż dyski HDD, w których głowica parkuje dosłownie co kilka sekund. Może i istnieją sposoby, by sobie poradzić z tym parkowaniem głowicy w dyskach HDD ale podejście producentów tych nośników do klientów sprawiło, że postanowiłem w końcu pokazać im środkowy palec i pod storage wybrać dysk SSD. Z racji, że w zasadzie rzadko kiedy z tego dysku coś będzie usuwane (chyba że dysk ulegnie zapełnieniu), to tą niezbyt dużą ilością cykli wymazywania/zapisywania komórek flash (około 350) nie ma zbytnio się co przejmować. Podobnie sprawa wygląda w kwestii wydajności, przynajmniej przy odczycie danych.
Po kilku miesiącach użytkowania tego dysku SSD zaszła potrzebna, by go nieco przeczyścić i tym samym zrobić nieco wolnego miejsca pod nowe filmy i seriale. Problem w tym, że transfer po sieci w pewnym momencie spadł mi ze 110 MiB/s do około 1-2 MiB/s i tak wisiał w menadżerze plików, a postępu w kopiowaniu danych nie było żadnych (no dobra prawie żadnych). Myślałem, że może coś się stało z procesem kopiowania (został przerwany czy zawiesił się) ale ten funkcjonował poprawnie. Podejrzenie padło więc na mechanizm TRIM. Okazało się, że ten dysk SSD ma TRIM zupełnie wyłączony, choć sam nośnik wsparcie dla TRIM posiada. Jak to możliwe? Wygląda na to, że w przypadku dysków SSD podpinanych do portów USB komputera, ten cały TRIM może nieść ze sobą bardzo niemiłe konsekwencje i dlatego jest domyślnie wyłączony.
By sprawdzić czy TRIM dla dysku SSD podpiętego do portu USB komputera jest aktywny i działa, możemy
albo skorzystać z polecenia fstrim
na systemie plików takiego nośnika, albo też możemy posłużyć
się poleceniem lsblk
.
W tym przypadku, polecenia fstrim
zwróciło komunikat o braku wsparcia dla TRIM:
# fstrim -v /media/morfik/gdata
fstrim: /media/morfik/gdata: the discard operation is not supported
Jeśli zaś zajrzymy do wyjścia polecenia lsblk
, to zobaczymy wartość 0B w kolumnie DISC-MAX
,
co też jednoznacznie nam mówi, że kernel wsparcia dla TRIM dla tego dysku z jakiegoś powodu nie
włączył:
# lsblk -D /dev/sdc
NAME DISC-ALN DISC-GRAN DISC-MAX DISC-ZERO
sdc 0 512B 0B 0
└─sdc1 0 512B 0B 0
Jest to o tyle dziwne, że model dysku SSD SSDPR-CX400-02T-G2
wsparcie dla mechanizmu TRIM jak
najbardziej posiada, co możemy odczytać z raportu
SMART ( TRIM Command: Available, deterministic, zeroed
):
# smartctl -x /dev/sdc
smartctl 7.4 2023-08-01 r5530 [x86_64-linux-6.9.4-amd64] (local build)
Copyright (C) 2002-23, Bruce Allen, Christian Franke, www.smartmontools.org
=== START OF INFORMATION SECTION ===
Device Model: SSDPR-CX400-02T-G2
Serial Number: G44034700
LU WWN Device Id: 5 000000 0000027b9
Firmware Version: SN17595
User Capacity: 2,048,408,248,320 bytes [2.04 TB]
Sector Size: 512 bytes logical/physical
Rotation Rate: Solid State Device
Form Factor: 2.5 inches
TRIM Command: Available, deterministic, zeroed
Device is: Not in smartctl database 7.3/5610
ATA Version is: ACS-4 (minor revision not indicated)
SATA Version is: SATA 3.2, 6.0 Gb/s (current: 6.0 Gb/s)
Local Time is: Sun Jun 16 10:39:14 2024 CEST
SMART support is: Available - device has SMART capability.
SMART support is: Enabled
AAM feature is: Unavailable
APM feature is: Unavailable
Rd look-ahead is: Enabled
Write cache is: Enabled
DSN feature is: Unavailable
ATA Security is: Disabled, NOT FROZEN [SEC1]
Wt Cache Reorder: Enabled
=== START OF READ SMART DATA SECTION ===
SMART overall-health self-assessment test result: PASSED
General SMART Values:
Offline data collection status: (0x00) Offline data collection activity
was never started.
Auto Offline Data Collection: Disabled.
Self-test execution status: ( 0) The previous self-test routine completed
without error or no self-test has ever
been run.
Total time to complete Offline
data collection: ( 33) seconds.
Offline data collection
capabilities: (0x7b) SMART execute Offline immediate.
Auto Offline data collection on/off support.
Suspend Offline collection upon new
command.
Offline surface scan supported.
Self-test supported.
Conveyance Self-test supported.
Selective Self-test supported.
SMART capabilities: (0x0003) Saves SMART data before entering
power-saving mode.
Supports SMART auto save timer.
Error logging capability: (0x01) Error logging supported.
General Purpose Logging supported.
Short self-test routine
recommended polling time: ( 2) minutes.
Extended self-test routine
recommended polling time: ( 85) minutes.
Conveyance self-test routine
recommended polling time: ( 2) minutes.
SCT capabilities: (0x0031) SCT Status supported.
SCT Feature Control supported.
SCT Data Table supported.
SMART Attributes Data Structure revision number: 20
Vendor Specific SMART Attributes with Thresholds:
ID# ATTRIBUTE_NAME FLAGS VALUE WORST THRESH FAIL RAW_VALUE
5 Reallocated_Sector_Ct PO--C- 100 100 010 - 0
9 Power_On_Hours -O--C- 100 100 000 - 273
12 Power_Cycle_Count -O--C- 100 100 000 - 100
164 Unknown_Attribute ------ 100 100 000 - 21476540424
165 Unknown_Attribute ------ 100 100 000 - 26
166 Unknown_Attribute ------ 100 100 000 - 5
167 Unknown_Attribute -O---K 100 100 000 - 8
194 Temperature_Celsius -O---K 029 029 000 - 29 (Min/Max 24/40)
199 UDMA_CRC_Error_Count -O--C- 100 100 000 - 0
241 Total_LBAs_Written -O--CK 100 100 000 - 1776
242 Total_LBAs_Read -O--CK 100 100 000 - 396
||||||_ K auto-keep
|||||__ C event count
||||___ R error rate
|||____ S speed/performance
||_____ O updated online
|______ P prefailure warning
General Purpose Log Directory Version 1
SMART Log Directory Version 1 [multi-sector log support]
Address Access R/W Size Description
0x00 GPL,SL R/O 1 Log Directory
0x01 SL R/O 1 Summary SMART error log
0x02 SL R/O 51 Comprehensive SMART error log
0x03 GPL R/O 64 Ext. Comprehensive SMART error log
0x04 GPL,SL R/O 8 Device Statistics log
0x06 SL R/O 1 SMART self-test log
0x07 GPL R/O 1 Extended self-test log
0x09 SL R/W 1 Selective self-test log
0x10 GPL R/O 1 NCQ Command Error log
0x11 GPL R/O 1 SATA Phy Event Counters log
0x30 GPL,SL R/O 9 IDENTIFY DEVICE data log
0x80-0x9f GPL,SL R/W 16 Host vendor specific log
0xe0 GPL,SL R/W 1 SCT Command/Status
0xe1 GPL,SL R/W 1 SCT Data Transfer
SMART Extended Comprehensive Error Log Version: 1 (64 sectors)
No Errors Logged
SMART Extended Self-test Log Version: 1 (1 sectors)
Num Test_Description Status Remaining LifeTime(hours) LBA_of_first_error
# 1 Extended offline Completed without error 00% 1 -
SMART Selective self-test log data structure revision number 1
SPAN MIN_LBA MAX_LBA CURRENT_TEST_STATUS
1 0 0 Not_testing
2 0 0 Not_testing
3 0 0 Not_testing
4 0 0 Not_testing
5 0 0 Not_testing
Selective self-test flags (0x0):
After scanning selected spans, do NOT read-scan remainder of disk.
If Selective self-test is pending on power-up, resume after 0 minute delay.
SCT Status Version: 3
SCT Version (vendor specific): 1 (0x0001)
Device State: Active (0)
Current Temperature: 32 Celsius
Power Cycle Min/Max Temperature: ?/32 Celsius
Lifetime Min/Max Temperature: ?/ ? Celsius
Under/Over Temperature Limit Count: 0/0
SCT Temperature History Version: 2
Temperature Sampling Period: 1 minute
Temperature Logging Interval: 1 minute
Min/Max recommended Temperature: -127/127 Celsius
Min/Max Temperature Limit: -127/127 Celsius
Temperature History Size (Index): 478 (369)
Index Estimated Time Temperature Celsius
370 2024-06-16 02:42 ? -
371 2024-06-16 02:43 30 ***********
...
369 2024-06-16 10:39 32 *************
SCT Error Recovery Control command not supported
Device Statistics (GP Log 0x04)
Page Offset Size Value Flags Description
0x01 ===== = = === == General Statistics (rev 1) ==
0x01 0x008 4 100 --- Lifetime Power-On Resets
0x01 0x010 4 273 --- Power-on Hours
0x01 0x018 6 3725681472 --- Logical Sectors Written
0x01 0x020 6 13529637 --- Number of Write Commands
0x01 0x028 6 832035662 --- Logical Sectors Read
0x01 0x030 6 3014905 --- Number of Read Commands
0x07 ===== = = === == Solid State Device Statistics (rev 1) ==
0x07 0x008 1 5 N-- Percentage Used Endurance Indicator
|||_ C monitored condition met
||__ D supports DSN
|___ N normalized value
Pending Defects log (GP Log 0x0c) not supported
SATA Phy Event Counters (GP Log 0x11)
ID Size Value Description
0x0001 2 0 Command failed due to ICRC error
0x0003 2 0 R_ERR response for device-to-host data FIS
0x0004 2 0 R_ERR response for host-to-device data FIS
0x0006 2 0 R_ERR response for device-to-host non-data FIS
0x0007 2 0 R_ERR response for host-to-device non-data FIS
0x0008 2 0 Device-to-host non-data FIS retries
0x0009 4 0 Transition from drive PhyRdy to drive PhyNRdy
0x000a 4 1 Device-to-host register FISes sent due to a COMRESET
0x000f 2 0 R_ERR response for host-to-device data FIS, CRC
0x0010 2 0 R_ERR response for host-to-device data FIS, non-CRC
0x0012 2 0 R_ERR response for host-to-device non-data FIS, CRC
0x0013 2 0 R_ERR response for host-to-device non-data FIS, non-CRC
Zatem wsparcie dla TRIM w firmware dysku SSD jest ale pozostaje ono nieaktywne w systemie. Problem tkwi w adapterze USB-SATA (czy też zewnętrznej obudowie USB), za pomocą którego ten nośnik SSD został podpięty do portu USB komputera. Najwyraźniej z jakiegoś powodu taka konfiguracja upośledza ten cały mechanizm TRIM.
Problematyczny adapter USB-SATA (obudowa USB)
Zgodnie z tym, co można wyczytać tutaj, taki adapter USB-SATA ma spełniać dwie główne funkcje. Pierwszą z nich jest interfejs UAS, który jest interfejsem USB do kapsułkowania (enkapsulacji) poleceń protokołu SCSI. Drugą zaś jest translacja, gdzie polecenia SCSI są konwertowane na polecenia ATA. Zestaw poleceń określonych przez protokół SCSI jest spory i nie wszystkie urządzenia SCSI wspierają wszystkie te polecenia. Dlatego też istnieje kilka poleceń, które mogą zostać wykorzystane do UNMAP (TRIM to polecenie ATA, UNMAP to jego analog SCSI). Translator SCSI-ATA może wspierać jedno z tych poleceń, może także wspierać kilka lub wszystkie ale też może nie wspierać żadnego. W przypadku tego ostatniego nie będzie możliwy UNMAP bloku dysku SSD. Dlatego też trzeba się zaopatrzyć w taki adapter USB-SATA, który wspiera choć jedno polecenie UNMAP.
Poniżej jest trochę informacji na temat samego adaptera USB-SATA (Unitek USB3.1 USB-A to 2.5" SATA6G):
# lsusb
...
Bus 004 Device 003: ID 174c:55aa ASMedia Technology Inc. ASM1051E SATA 6Gb/s bridge, ASM1053E SATA 6Gb/s bridge, ASM1153 SATA 3Gb/s bridge, ASM1153E SATA 6Gb/s bridge
...
# lsusb -t
...
/: Bus 004.Port 001: Dev 001, Class=root_hub, Driver=xhci_hcd/4p, 5000M
|__ Port 001: Dev 003, If 0, Class=Mass Storage, Driver=uas, 5000M
...
# lsusb -vvv -d 174c:55aa
Bus 004 Device 003: ID 174c:55aa ASMedia Technology Inc. ASM1051E SATA 6Gb/s bridge, ASM1053E SATA 6Gb/s bridge, ASM1153 SATA 3Gb/s bridge, ASM1153E SATA 6Gb/s bridge
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 3.10
bDeviceClass 0 [unknown]
bDeviceSubClass 0 [unknown]
bDeviceProtocol 0
bMaxPacketSize0 9
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 asmedia
iProduct 3 ASMT1153e
iSerial 1 123456789394
bNumConfigurations 1
Configuration Descriptor:
bLength 9
bDescriptorType 2
wTotalLength 0x0079
bNumInterfaces 1
bConfigurationValue 1
iConfiguration 0
bmAttributes 0xc0
Self Powered
MaxPower 0mA
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 0
bAlternateSetting 0
bNumEndpoints 2
bInterfaceClass 8 Mass Storage
bInterfaceSubClass 6 SCSI
bInterfaceProtocol 80 Bulk-Only
iInterface 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x81 EP 1 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0400 1x 1024 bytes
bInterval 0
bMaxBurst 15
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x02 EP 2 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0400 1x 1024 bytes
bInterval 0
bMaxBurst 15
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 0
bAlternateSetting 1
bNumEndpoints 4
bInterfaceClass 8 Mass Storage
bInterfaceSubClass 6 SCSI
bInterfaceProtocol 98
iInterface 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x81 EP 1 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0400 1x 1024 bytes
bInterval 0
bMaxBurst 15
MaxStreams 32
Data-in pipe (0x03)
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x02 EP 2 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0400 1x 1024 bytes
bInterval 0
bMaxBurst 15
MaxStreams 32
Data-out pipe (0x04)
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x83 EP 3 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0400 1x 1024 bytes
bInterval 0
bMaxBurst 15
MaxStreams 32
Status pipe (0x02)
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x04 EP 4 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0400 1x 1024 bytes
bInterval 0
bMaxBurst 0
Command pipe (0x01)
Binary Object Store Descriptor:
bLength 5
bDescriptorType 15
wTotalLength 0x0016
bNumDeviceCaps 2
USB 2.0 Extension Device Capability:
bLength 7
bDescriptorType 16
bDevCapabilityType 2
bmAttributes 0x0000f41e
BESL Link Power Management (LPM) Supported
BESL value 1024 us
Deep BESL value 61440 us
SuperSpeed USB Device Capability:
bLength 10
bDescriptorType 16
bDevCapabilityType 3
bmAttributes 0x00
wSpeedsSupported 0x000e
Device can operate at Full Speed (12Mbps)
Device can operate at High Speed (480Mbps)
Device can operate at SuperSpeed (5Gbps)
bFunctionalitySupport 1
Lowest fully-functional device speed is Full Speed (12Mbps)
bU1DevExitLat 10 micro seconds
bU2DevExitLat 2047 micro seconds
can't get debug descriptor: Resource temporarily unavailable
Device Status: 0x000d
Self Powered
U1 Enabled
U2 Enabled
Ryzyko utraty danych i uszkodzenia dysku SSD
Trzeba tutaj wyraźnie rozgraniczyć dwie rzeczy. To, że w firmware dysku SSD zaimplementowano wsparcie dla TRIM nie oznacza z automatu, że kernel linux'a będzie do takiego kośnika przesyłał takie żądania, przez co sam nośnik SSD może (i prawdopodobnie będzie) nam się zachowywać tak, jakby wsparcia dla TRIM nie posiadał. Przyczyny mogą być różne, choć ta najpopularniejsza to błędy w komunikacji między kernelem a dyskiem, co może prowadzić do uwalenia dysku lub utraty zgromadzonych na nim danych. Dlatego też jeśli zaistnieją przesłanki, że TRIM nie powinien być obsługiwany dla tego konkretnego dysku SSD, to kernel go nie włącza. Z reguły kernel linux'a ma dobre powody by nie włączać na pewnych nośnika SSD obsługi mechanizmu TRIM. Niemniej jednak, w pewnych przypadkach decyzja podjęta przez kernel może być niewłaściwa i trzeba będzie taki nośnik SSD oznaczyć ręcznie, by kernel na nim TRIM włączył.
Jeśli nie jesteśmy przekonani co do włączania TRIM dla dysków SSD zamkniętych w zewnętrznych
obudowach USB czy też podłączanych za pomocą adaptera USB-SATA, to najlepszym rozwiązaniem dla nas
będzie wypięcie takiego nośnika z obudowy/adaptera i podłączenie go do slotu SATA w komputerze i
dopiero w takim przypadku zainicjowanie TRIM przez wydanie polecenia fstrim
na podmontowanym
systemie plików. W taki sposób nie ma ryzyka utraty danych czy uszkodzenia samego dysku SSD,
niemniej jednak jeśli zapisujemy dużo danych na dysku, to takie ciągłe przepinanie mija się raczej
z celem.
Strony VPD (Vital Product Data)
Czytając dalej ten podlinkowany wyżej artykuł, możemy wyłapać informację, że ze względu na bogactwo zestawu poleceń SCSI, sterownik kernela linux musi wiedzieć jakie funkcje są obsługiwane przez dane urządzenie. Odbywa się to za pomocą stron informacyjnych, zwanych stronami VPD (Vital Product Data), zwracanych przez urządzenie do sterownika.
Pierwszą stroną jest Supported VPD pages
, która zawiera listę stron obsługiwanych przez
dane urządzenie. Zwykle sterownik wysyła zapytanie o tę pierwszą stronę, a następnie o
interesujące go strony, jeśli są one obsługiwane. Jeśli chodzi o funkcję UNMAP, interesującą nas
stroną jest strona Logical block provisioning
.
Na linux'ach jesteśmy w stanie wysłać zapytanie o strony Supported VPD pages
oraz
Logical block provisioning
przy pomocy narzędzia sg_vpd
(z pakietu sg3-utils
). Musimy
jedynie określić dysk, do którego zapytania powędrują:
Dla strony Supported VPD pages
:
# sg_vpd -p bl /dev/sdc
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]
Dla strony Logical block provisioning
:
# sg_vpd --page=0xb2 /dev/sdc
# sg_vpd -p lbpv /dev/sdc
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]
To co się rzuca w oczy, to fakt, że translator SCSI-ATA wspiera polecenie
UNMAP ( Unmap command supported (LBPU)
) ale nie wspiera poleceń WRITE SAME,
tj. ( Write same (16)
i Write same (10)
). Będzie to miało znaczenie w późniejszym procesie
konfiguracji TRIM dla tego nośnika SSD.
Brak auto konfiguracji w oparciu o VPD
Normalnie sterownik odczytałby te strony i odpowiednio skonfigurował urządzenie. Niemniej jednak, istnieje ryzyko, że próba odczytania niektórych stron VPD może doprowadzić do zablokowania (lock-up) lub też wręcz uszkodzenia nośnika SSD. Dlatego też deweloperzy kernela linux zdecydowali się domyślnie nie odpytywać stron VPD dla urządzeń SCSI podłączonych przez USB i nie konfigurować zaawansowanych funkcji, takich jak UNMAP.
Jak włączyć TRIM w dysku SSD na USB
W tym przypadku mamy do czynienia z dyskiem SSD od Goodram, konkretnie jest to
model SSDPR-CX400-02T-G2
. Poniższe instrukcje zostały przetestowane tylko i wyłączenie z tym
dyskiem i jeśli nie mamy pewności co do obsługi TRIM/UNMAP w przypadku naszego dysku/adaptera
USB-SATA, to lepiej jest tych poniższych kroków nie przeprowadzać.
Ręczne określenie wartości w provisioning_mode (konfiguracja UNMAP)
Dobra wiadomość jest taka, że te powyżej opisane funkcje można skonfigurować z przestrzeni
użytkownika używając do tego celu pliku /sys/block/*/device/scsi_disk/*/provisioning_mode
lub /sys/devices/**/scsi_disk/**/provisioning_mode
. Gdy podłączymy dysk SSD za pomocą adaptera
USB-SATA do portu USB komputera, to wartość w tym pliku będzie prawdopodobnie wskazywać na full
:
# find /sys/ -name provisioning_mode -exec grep -H . {} + | sort
/sys/devices/pci0000:00/0000:00:14.0/usb4/4-1/4-1:1.0/host6/target6:0:0/6:0:0:0/scsi_disk/6:0:0:0/provisioning_mode:full
...
Możemy jednak tę wartość przestawić ręcznie zapisując powyższy plik. W tym przypadku pozostaje nam
w zasadzie jedynie unmap
ponieważ ten dysk podłączony przez adapter USB-SATA obsługuje jedynie
polecenie UNMAP. Można także określić wartości (jeśli są wspierane ) writesame_16
, writesame_10
, writesame_zero
oraz disabled
.
W przypadku, gdy mamy więcej niż jeden dysk, dobrze jest upewnić się, że ta ścieżka powyżej jest odpowiednia:
# lsscsi -liv
...
[6:0:0:0] disk SSDPR-CX 400-02T-G2 0 /dev/sdc SSDPR-CX_400-02T-G2_123456789394-0:0
state=running queue_depth=30 scsi_level=7 type=0 device_blocked=0 timeout=30
dir: /sys/bus/scsi/devices/6:0:0:0 [/sys/devices/pci0000:00/0000:00:14.0/usb4/4-1/4-1:1.0/host6/target6:0:0/6:0:0:0]
list_ndevices: scandir: /sys/class/nvme/: No such file or directory
NVMe module may not be loaded
Mając ustaloną ścieżkę oraz informację, że adapter USB-SATA wspiera jedynie polecenie UNMAP, zapisujemy stosowny plik w poniższy sposób:
# echo unmap > /sys/devices/pci0000:00/0000:00:14.0/usb4/4-1/4-1:1.0/host6/target6:0:0/6:0:0:0/scsi_disk/6:0:0:0/provisioning_mode
Sprawdzamy, czy wartość została poprawnie ustawiona:
# cat /sys/devices/pci0000:00/0000:00:14.0/usb4/4-1/4-1:1.0/host6/target6:0:0/6:0:0:0/scsi_disk/6:0:0:0/provisioning_mode
unmap
Wartości dla discard_max_bytes i discard_max_hw_bytes
Sprawdzamy także co zostało ustawione w pliku discard_max_bytes
i w discard_max_hw_bytes
,
które po włączeniu UNMAP zostaną automatycznie dostosowane (była tam wartość 0
):
# cat /sys/block/sdc/queue/discard_max_bytes
4294966784
# cat /sys/block/sdc/queue/discard_max_hw_bytes
4294966784
Zgodnie z tym co można wyczytać na wiki Gentoo, wartość którą widzimy tutaj, to ilość bajtów, które mechanizm TRIM będzie w stanie wyczyścić w jednym podejściu. Możemy ją obliczyć posługując się następującymi danymi:
# sg_vpd -p bl /dev/sdc
...
Maximum unmap LBA count: 4194240
...
oraz
# sg_readcap -l /dev/sdc
...
Logical block length=512 bytes
...
Mając informację o Maximum unmap LBA count
oraz Logical block length
, możemy ich wartości
pomnożyć przez siebie:
# echo '4194240*512' | bc
2147450880
Trochę to dziwne, że otrzymana wartość jest inna od tej domyślnie ustawionej. Nie wiem czy powinno
się zawierzyć tej automatycznie wykalkulowanej wartości, czy też tej obliczonej ręcznie. W każdym
razie jeśli nie chcemy zawierzać tej automatycznej, to wynik tego powyższego obliczenia możemy
przesłać do pliku /sys/block/sdc/queue/discard_max_bytes
:
# echo 2147450880 > /sys/block/sdc/queue/discard_max_bytes
Przeglądając pliki discard_max_bytes
innych moich dysków SSD, wygląda na to, że jednak
wartość 2147450880
jest pewniejsza, bo inne nośniki mają ustawione właśnie 2147450880
. Jedyne
co mi przychodzi do głowy, to że ta różnica może wynikać ze sporo większego rozmiaru dysku
SSD (256GB vs. 2TB).
W zasadzie to te dwa pliki ( discard_max_bytes
(RW) i discard_max_hw_bytes
(RO) ) służą jedynie
do zmniejszenia ilości danych/bloków, które mogą zostać wyczyszczone w pojedynczej operacji TRIM.
Czasem może się zdarzyć tak, że gdy system będzie czyścił zbyt wiele bloków naraz, to wzrosną nam
dość znacznie opóźnienia w zapytaniach do dysku SSD, co zdegraduje jego wydajność. Dlatego
istnieje taki mechanizm bezpieczeństwa w kernelu, by ilość tych danych ograniczyć, co powinno
zredukować opóźnienia. Ja bym pozostawił wartość domyślną i jedynie w przypadku ewentualnej
odczuwalnej degradacji wydajności dysku SSD by próbował tę wartość w pliku discard_max_bytes
przepisać.
Tak czy inaczej w wyjściu lsblk
na pozycji DISC-MAX
powinna już być widoczna jakaś wartość:
# lsblk --discard /dev/sdc
NAME DISC-ALN DISC-GRAN DISC-MAX DISC-ZERO
sdc 0 512B 4G 0
└─sdc1 0 512B 4G 0
Test TRIM dysku SSD na USB
W zasadzie jedyne co zrobiliśmy w przypadku tego dysku SSD to zapisaliśmy unmap
w
pliku provisioning_mode
i to powinno włączyć mechanizm TRIM dla tego nośnika SSD podpiętego przez
ten adapter USB-SATA do portu USB komputera. Czy tak się faktycznie stało, możemy ocenić montując
system plików dowolnej partycji dysku i wydając w terminalu polecenie fstrim
:
# fstrim -v /media/morfik/gdata
/media/morfik/gdata: 482.5 GiB (518127964160 bytes) trimmed
Jak widać tym razem polecenie fstrim
na tym dysku zadziałało. W zależności od ilości bloków
przeznaczonych do wyczyszczenia, to powyższe polecenie może zająć dłuższą chwilę. Po jego
wykonaniu dobrze jest dać dyskowi chwilę na ogarnięcie się. Z tego co zaobserwowałem, to
bezpośrednio po wykonaniu polecenia fstrim
wydajność dysku nie powraca od razu do tej wyjściowej.
Potrzebnych jest kilka-kilkanaście dodatkowych minut, by transfer z paru MiB/s powrócił do tego, co
było przed zapełnieniem się dysku.
Po odczekaniu chwili, odmontowujemy partycję i ponownie ją montujemy. Jeśli jesteśmy w stanie odczytać dane zgromadzone na dysku SSD, to znaczy, że wszystko jest w porządku i możemy przejść do napisania reguły dla UDEV'a dla tego nośnika.
Reguła dla UDEV
Sposób z zapisywaniem pliku provisioning_mode
ma efekt jedynie tymczasowy i działa jedynie do
momentu odłączenia dysku SSD od komputera. Po ponownym podłączeniu trzeba by ponownie zapisać
wspomniany plik. Jest to mało praktyczne i przydałoby się napisać regułę dla UDEV'a, która za nas
automatycznie dostosuje ten plik po podpięciu dysku do portu USB.
Edytujemy (lub tworzymy) plik /etc/udev/rules.d/10-trim.rules
i dodajemy do niego poniższą
zawartość:
# SSD disk Goodram SSDPR-CX400-02T-G2 over Unitek USB3.1 USB-A to 2.5" SATA6G USB-SATA adapter
#
ACTION=="add|change", ATTRS{idVendor}=="174c", ATTRS{idProduct}=="55aa", SUBSYSTEM=="scsi_disk", ATTR{provisioning_mode}="unmap"
Przeładowujemy politykę UDEV'a i patrzymy czy plik provisioning_mode
zawiera już odpowiednią
wartość (dla pewności dobrze jest odłączyć dysk od portu USB i podpiąć go ponownie):
# udevadm control --reload-rules
# udevadm trigger --type=devices --action=change
# cat /sys/devices/pci0000:00/0000:00:14.0/usb4/4-1/4-1:1.0/host6/target6:0:0/6:0:0:0/scsi_disk/6:0:0:0/provisioning_mode
unmap
Jak widać, unmap
został już ustawiony automatycznie.
Podsumowanie
Przyznam, że gdy kupowałem dysk SSD z zamiarem używania go po USB jako storage, nie byłem świadomy, że TRIM w takiej sytuacji nie będzie działał z automatu oraz, że próba jego włączenia może okazać się katastrofalna w skutkach. Na szczęście, w przypadku tego dysku SSD nic złego się nie wydarzyło, dane na dysku są i można je odczytać, a sam nośnik działa do tej pory, z nieco większą wydajnością. Dlatego też jeśli zamierzamy kupić dysk SSD i podłączać go przez adaptery/obudowy USB-SATA, to lepiej upewnić się, że ta obudowa czy adapter wspierają TRIM/UNMAP, bo jeśli tak nie będzie, to trzeba będzie liczyć się z transferami rzędu pojedynczych MiB z chwilą, gdy dysk po raz pierwszy się zapełni.