dt.iki.fi

Disable a USB port and the connected hard drive

And enable it when required.

Also see this related article.

Premise & problem

I have a USB hard drive used only for weekly automated backups. It is always connected to the same port and never gets removed. At all other times I want this drive to be unmounted, off, invisible, powered down. I want to save its limited lifetime by keeping it powered down, and I don't want any process to power it up again. Unfortunately various systemd services are prone to do just that, e.g. systemd-hostnamed, dbus, udisks2, but also command line utilities like fdisk, smartctl and even hdparm itself. Waking the drive takes 30s which leads to unholy delays in certain actions (opening files with GtkFileChooser).
Additionally this cheap WD passport drive has very limited powersaving capabilities.

Only the script running the backup should be allowed to power it up again, mount its partition, do the backup, then unmount and power down.

I had hoped a udev rule would be sufficient, but the described issues were still cropping up.

Solution

Off

We will send the hard drive to sleep with hdparm and then completely switch off that particular USB socket:

# /usr/bin/hdparm -Y "$device"

I don't recommend using kernel assigned drive letters like /dev/sdX because they can change. Instead I found the device in /dev/disk/by-id/.

That was easy.
Now, find the USB port. There's probably different ways, but I went with /sys/bus/usb/devices/X-X. Those two numbers (e.g. 2-6 or 1-4 etc.) are also reflected in the output of lsusb -t (a command I used dozens of times during testing).

# echo "0" > /sys/bus/usb/devices/X-X/power/autosuspend_delay_ms
# echo "auto" > /sys/bus/usb/devices/X-X/power/control

And lastly, unbind the USB port in question:

# echo X-X > /sys/bus/usb/drivers/usb/unbind

This does indeed not power the USB port off, but it's almost completely invisible to the system now and the drive can slumber in peace.

I had a lot of help from this SO article.

On

Just perform the steps in reverse order:

# echo X-X > /sys/bus/usb/drivers/usb/bind
# echo "2000" > /sys/bus/usb/devices/X-X/power/autosuspend_delay_ms
# echo "on" > /sys/bus/usb/devices/X-X/power/control

"2000" and "on" are default values as per kernel documentation.

Files

The script and an example systemd service file can be found in my stuff repository. Copy hd-usb.service to /etc/systemd/system/ and edit to your requirements. Enable the service so it becomes active after a reboot:

# systemctl enable hd-usb

Or start it now:

# systemctl start hd-usb