No requirement or problem, just curious!
Trying to do what udisks does, without using udisks. I know nothing about hardware things and would like to understand better what happens in my experiments.
Searching the web, this question has come up several times over the years and it baffles me how it usually seems to remain unanswered and how there is no easier method?
Questions:
- Are my steps below on the right track? Is there better options? A dangerous mistake?
- How do we verify the results of the device buffer sync calls?
- See the dump below: Why does hdparm -Y seem to succeed, while the results of sdparm --command=stop seem unexpected in my case? Despite sdparm using the correct(?) ioctl flags?
- Is writing directly to /sys/block/sda/device/delete recommended? Does it utilize any internal safeguards?
Looking at this function:
udisks/udiskslinuxdrive.c:handle_power_off
I gather these steps:
- Whine on drive in use
- fsync
- ioctl SCSI SYNCHRONIZE CACHE
- ioctl SCSI START STOP UNIT
- Write '1' to /sys/block/.../device/remove
Which aligns with the manual:
$ man udisksctl:
power-off
Arranges for the drive to be safely removed and powered off. On the OS side this includes ensuring that no process is using the drive, then requesting that in-flight buffers and caches are committed to stable storage. The exact steps for powering off the drive depends on the drive itself and the interconnect used. For drives connected through USB, the effect is that the USB device will be deconfigured followed by disabling the upstream hub port it is connected to.
Note that as some physical devices contain multiple drives (for example 4-in-1 flash card reader USB devices) powering off one drive may affect other drives. As such there are not a lot of guarantees associated with performing this action. Usually the effect is that the drive disappears as if it was unplugged.
Trying to replicate the udisks steps, I came up with:
- # eject /dev/sda
- sync dance
- # sdparm --command=sync /dev/sda // Verify result?
or hdparm -f /dev/sda // Verify result?
or hdparm -F /dev/sda // Verify result? - # hdparm -Y /dev/sda
or sdparm --command=stop /dev/sda // Spins down, then restarts?
or sdparm --command=eject /dev/sda // Does nothing? - # printf '1' > /sys/block/sda/device/delete
Step 1.
AFAIK 'eject' should sync, then unmount all partitions?
Step 2.
The myth as passed down from our elders.
Looking at hdparm -f:
hdparm/hdparm.c:
static void flush_buffer_cache (int fd)
{
sync();
fsync(fd); /* flush buffers */
fdatasync(fd); /* flush buffers */
sync();
if (ioctl(fd, BLKFLSBUF, NULL)) /* do it again, big time */
perror("BLKFLSBUF failed");
else
do_drive_cmd(fd, NULL, 0); /* IDE: await completion */
sync();
}
Step 3.
hdparm and sdparm finally call ioctl, but hdparm with different flags.
udisks/udiskslinuxdrive.c:
cdb[0] = 0x35; /* OPERATION CODE: SYNCHRONIZE CACHE (10) */
sdparm/lib/sg_cmds_basic2.c:
#define SYNCHRONIZE_CACHE_CMD 0x35
sdparm/lib/sg_pt_linux.c:do_scsi_pt_v3
if (ioctl(fd, SG_IO, &v3_hdr) < 0) {
hdparm/sgio.h:
ATA_OP_FLUSHCACHE = 0xe7,
ATA_OP_FLUSHCACHE_EXT = 0xea,
hdparm/sgio.c:do_drive_cmd
#ifdef SG_IO
rc = sg16(fd, SG_READ, is_dma(tf.command), &tf, data, data_bytes, timeout_secs);
use_legacy_ioctl:
#endif /* SG_IO */
return ioctl(fd, HDIO_DRIVE_CMD, args);
hdparm/sgio.c:sg16
if (ioctl(fd, SG_IO, &io_hdr) == -1) {
I set my eyes on hdparm and sdparm because:
$ info ioctl
Although some such objects such as sockets and terminals (1) have
special functions of their own, it would not be practical to create
functions for all these cases.
Instead these minor operations, known as “IOCTL”s, are assigned code
numbers and multiplexed through the ‘ioctl’ function, defined in
‘sys/ioctl.h’. The code numbers themselves are defined in many
different headers.
-- Function: int ioctl (int FILEDES, int COMMAND, ...)
Step 4.
Like above, sdparm uses the flags udisks does, hdparm is different:
udisks/udiskslinuxdrive.c:
cdb[0] = 0x1b; /* OPERATION CODE: START STOP UNIT */
sdparm/lib/sg_cmds_basic2.c:
#define START_STOP_CMD 0x1b
hdparm/sgio.h:
ATA_OP_SLEEPNOW2 = 0x99,
ATA_OP_SLEEPNOW1 = 0xe6,
According to https://sg.danny.cz/sg/sdparm.html
But is this true?
The udisks/sdparm flags should be preferred?
https://t10.org/ftp/t10/document.05/05-344r0.pdf
Step 5.
udisks/udiskslinuxdrive.c:handle_power_off
/* http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git;a=commit;h=253e05724f9230910344357b1142ad8642ff9f5a */
remove_path = g_strdup_printf ("%s/remove", g_udev_device_get_sysfs_path (usb_device));
f = fopen (remove_path, "w");
...
gchar contents[1] = {'1'};
if (fwrite (contents, 1, 1, f) != 1)
There is no /remove provided for my device.
/delete appears to have the same effect but I do not know how to verify?
Test step 1:
$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
nvme0n1 259:0 0 238.5G 0 disk
├─nvme0n1p1 259:1 0 50M 0 part /boot/efi
├─nvme0n1p2 259:2 0 20G 0 part /
└─nvme0n1p3 259:3 0 218.4G 0 part /home
$ lsusb
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 002: ID 413c:1010 Dell Computer Corp. USB 2.0 Hub [MTT]
Bus 001 Device 005: ID 413c:2110 Dell Computer Corp. Dell Wired Multimedia Keyboard
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Connect via USB
[2339994.556770] usb 2-1: new SuperSpeed USB device number 10 using xhci_hcd
[2339994.571244] usb 2-1: New USB device found, idVendor=0411, idProduct=0240, bcdDevice= 1.23
[2339994.571254] usb 2-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[2339994.571257] usb 2-1: Product: HD-PCFU3
[2339994.571260] usb 2-1: Manufacturer: BUFFALO
[2339994.571263] usb 2-1: SerialNumber: 00000314000060E5
[2339994.572062] usb-storage 2-1:1.0: USB Mass Storage device detected
[2339994.572384] scsi host2: usb-storage 2-1:1.0
[2339995.581576] scsi 2:0:0:0: Direct-Access BUFFALO HD-PCFU3 0000 PQ: 0 ANSI: 3
[2340002.309121] sd 2:0:0:0: [sda] 1953525168 512-byte logical blocks: (1.00 TB/932 GiB)
[2340002.309974] sd 2:0:0:0: [sda] Write Protect is off
[2340002.309978] sd 2:0:0:0: [sda] Mode Sense: 73 00 10 08
[2340002.311189] sd 2:0:0:0: [sda] Write cache: enabled, read cache: enabled, supports DPO and FUA
[2340002.329842] sda: sda1
[2340002.330025] sd 2:0:0:0: [sda] Attached SCSI disk
$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 931.5G 0 disk
└─sda1 8:1 0 931.5G 0 part
nvme0n1 259:0 0 238.5G 0 disk
├─nvme0n1p1 259:1 0 50M 0 part /boot/efi
├─nvme0n1p2 259:2 0 20G 0 part /
└─nvme0n1p3 259:3 0 218.4G 0 part /home
$ lsusb
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 002: ID 413c:1010 Dell Computer Corp. USB 2.0 Hub [MTT]
Bus 001 Device 005: ID 413c:2110 Dell Computer Corp. Dell Wired Multimedia Keyboard
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 002 Device 010: ID 0411:0240 BUFFALO INC. (formerly MelCo., Inc.) HD-PCFU3
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
# lsusb -v -D /dev/bus/usb/002/010
Device: ID 0411:0240 BUFFALO INC. (formerly MelCo., Inc.) HD-PCFU3
Negotiated speed: SuperSpeed (5Gbps)
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 3.00
bDeviceClass 0 [unknown]
bDeviceSubClass 0 [unknown]
bDeviceProtocol 0
bMaxPacketSize0 9
idVendor 0x0411 BUFFALO INC. (formerly MelCo., Inc.)
idProduct 0x0240 HD-PCFU3
bcdDevice 1.23
iManufacturer 1 BUFFALO
iProduct 2 HD-PCFU3
iSerial 3 00000314000060E5
bNumConfigurations 1
Configuration Descriptor:
bLength 9
bDescriptorType 2
wTotalLength 0x002c
bNumInterfaces 1
bConfigurationValue 1
iConfiguration 0
bmAttributes 0x80
(Bus Powered)
MaxPower 896mA
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 0x01 EP 1 OUT
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 0x82 EP 2 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0400 1x 1024 bytes
bInterval 0
bMaxBurst 15
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 0x00000000
(Missing must-be-set LPM bit!)
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 1000 micro seconds
Device Status: 0x0000
(Bus Powered)
$ tail -n2 /etc/fstab
UUID=5C74B9B374B98FEE /home/shr/mount/aya ntfs3 rw,noauto,user 0 2
$ mount /dev/sda1
$ tail -n1 /etc/mtab
/dev/sda1 /home/shr/mount/aya ntfs3 rw,nosuid,nodev,noexec,relatime,uid=1000,gid=1000,iocharset=utf8 0 0
$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 931.5G 0 disk
└─sda1 8:1 0 931.5G 0 part /home/shr/mount/aya
nvme0n1 259:0 0 238.5G 0 disk
├─nvme0n1p1 259:1 0 50M 0 part /boot/efi
├─nvme0n1p2 259:2 0 20G 0 part /
└─nvme0n1p3 259:3 0 218.4G 0 part /home
# eject /dev/sda
eject: unable to eject
$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 931.5G 0 disk
└─sda1 8:1 0 931.5G 0 part
nvme0n1 259:0 0 238.5G 0 disk
├─nvme0n1p1 259:1 0 50M 0 part /boot/efi
├─nvme0n1p2 259:2 0 20G 0 part /
└─nvme0n1p3 259:3 0 218.4G 0 part /home
[2342632.956244] sda: sda1
Test step 2:
$ cat /proc/meminfo | grep -ie 'dirty'
Dirty: 267196 kB
$ sync
$ cat /proc/meminfo | grep -ie 'dirty'
Dirty: 20 kB
Test step 3:
# sdparm --command=sync /dev/sda
/dev/sda: BUFFALO HD-PCFU3 0000
$ cat /proc/meminfo | grep -ie 'dirty'
Dirty: 44 kB
Test step 4:
# sdparm --command=stop /dev/sda
/dev/sda: BUFFALO HD-PCFU3 0000
Short click sound, no change in device spin sound and LED.
[2343000.385143] sda: sda1
$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 931.5G 0 disk
└─sda1 8:1 0 931.5G 0 part
nvme0n1 259:0 0 238.5G 0 disk
├─nvme0n1p1 259:1 0 50M 0 part /boot/efi
├─nvme0n1p2 259:2 0 20G 0 part /
└─nvme0n1p3 259:3 0 218.4G 0 part /home
# sdparm --command=eject /dev/sda
/dev/sda: BUFFALO HD-PCFU3 0000
Did not notice any reaction from sdparm eject.
# hdparm -Y /dev/sda
SG_IO: bad/missing sense data, sb[]: 70 00 01 00 00 00 00 0a 00 00 00 00 00 1d 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
$ sg_decode_sense 70 00 01 00 00 00 00 0a 00 00 00 00 00 1d 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0
0 00 00
Fixed format, current; Sense key: Recovered Error
Additional sense: ATA pass through information available
error=0x0, status=0x0, device=0x0, count(7:0)=0x0
extend=0, log_index=0x0, lba_high,mid,low(7:0)=0x0,0x0,0x0
$ man sdparm | grep -e 'recovered error'
21 the DEVICE reports a "recovered error". The requested command was successful. Most likely a utility will report a recovered error to stderr and continue, probably leaving the utility with an exit status of 0 .
Device spin sound goes quiet, LED stays on
$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 931.5G 0 disk
└─sda1 8:1 0 931.5G 0 part
nvme0n1 259:0 0 238.5G 0 disk
├─nvme0n1p1 259:1 0 50M 0 part /boot/efi
├─nvme0n1p2 259:2 0 20G 0 part /
└─nvme0n1p3 259:3 0 218.4G 0 part /home
# lsusb -v -D /dev/bus/usb/002/010 | tail -n2
Device Status: 0x0000
(Bus Powered)
Test step 5:
# printf '1' > /sys/block/sda/device/delete
[2343533.184469] sd 2:0:0:0: [sda] Synchronizing SCSI cache
Device does otherwise not seem to react at all, LED still on
$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
nvme0n1 259:0 0 238.5G 0 disk
├─nvme0n1p1 259:1 0 50M 0 part /boot/efi
├─nvme0n1p2 259:2 0 20G 0 part /
└─nvme0n1p3 259:3 0 218.4G 0 part /home
Disconnect from USB
[2343580.588884] usb 2-1: reset SuperSpeed USB device number 10 using xhci_hcd
[2343581.278968] usb 2-1: USB disconnect, device number 10
Carpe noctem!