My words on free/open source software and Mac

Saturday, January 03, 2009

Safely remove an USB hard drive in Linux

I bought a new USB hard drive, Western Digital My Book® Essential Edition™ 1TB, and use it with Linux. Here I'm discussing about how to safely remove (then disconnect) it from a Linux system.

The problem: according to its user manual, you should disconnect the drive safely when it is not active, but in a Linux system how to do this is not very straightforward. You may notice that after you unmount it (whether through command-line or a desktop environment), the drive is still spinning and it's LED on. If you read it's user manual carefully you'll find that the manufacturer never said it's safe to disconnect it from your system in such condition.

The quick solution to this problem:

  1. send SYNC, then STOP command to the device, this can be done easily in Linux by unbinding the device

  2. suspend the USB port by echoing a "suspend" to the "/sys/bus/usb/devices/$DEVICE/power/level", where $DEVICE corresponding to the device of your USB device.


To put it simple, I wrote a script to do this for you. Download and try it. For developers, you can get it from the github repo.

If you saw an error message like this when running it:

bash: /sys/bus/usb/devices/5-8/power/level: No such file or directory

that means your kernel doesn't support USB suspend. You can just disconnect your device now, it's already safer enough to unplug it.

To those who is interested, discussion of technical detail following.

From the hardware aspect, the drive should only be removed when the following requirements are met:

  1. there's no pending I/O request and software cache flushed

  2. it's hardware cache flushed

  3. it's driver spins down (means "not spinning")

  4. (optional) the USB port is put into "suspend" mode



The user's manual read (page 6, section "Turning Off/Disconnecting the Device"): "(for Windows systems): Right-click the Safely Remove Hardware icon in your system tray and select Safely Remove Hardware. You may hear the drive power down before the Power LED turns off. The drive is now shut down properly, and you may disconnect the drive safely. (for Macintosh systems) Drag the My Book icon to the Trash icon for proper dismount. You may hear the drive power down as the Power LED flashes. When the Power LED is steady, you may press the Power button once or disconnect the drive’s power cord to turn off the drive safely." (there's a mistake, this device doesn't have a Power button) If you read carefully you can tell that Windows and Macintosh don't do the same things when told to remove an USB device. As the bold text as shown, in a Windows system it will be turned off while in a Macintosh system you hear the drive power down but the Power LED would finally become steady rather than off. Therefore we know that Windows does all 4 steps and Macintosh do the first 3 steps only when told to remove a USB device. (BTW, it's funny that you have to use your ear before disconnecting a device.)

(This is only the case for Windows XP. For Windows Vista seems only the first 3 steps are done thus the drive remained active after you removing it from the system.)


Some may argue that they always disconnect their drives directly after unmounting (step 1 only) and never experience any damage, but as an operating system developer and a serious user, I always follow the manufacturer's manual or specification whenever it make sense and possible because I know electronic devices are fragile, rigid and obtuse if you don't use them as the way they are designed. For a well-designed and robust piece of hardware, theoretically you can disconnect the device safely when the first two steps are done. The firmware on the device would notice the USB cable was unplugged and shut down the disk drive properly. If not, the drive would be stopped sharply as if you pulled it's power when it's running and this damages the hardware gradually. As an outsider of the manufacturer, we hardly know whether it's well-designed or not so the only safe principle is to follow it's manual, means we should not disconnect the device until at least all three steps are done.

At the Linux kernel part. To finish all the 4 steps, you have to unmount the device, unbind the device from the driver, then tell the USB core driver to put that device into suspend mode, as shown in the solution above. And you have to be running a kernel with CONFIG_USB_SUSPEND enabled.

CONFIG_USB_SUSPEND is not enabled by default in a vanilla kernel. I'm pushing it in this discussion.

Properly removing a device from a running Linux should be done by either the HAL or desktop manager. If you can, please push the developers you know working on any DM that missing this feature to implement this function properly.

Try my script, and write me if you found a problem. Welcome patches.

87 comments:

Chris said...

Glad I found this page since I have the exact same problem. Wouldn't it make sense to solve this with a udev rule?

Yan Li said...

To Chris: thanks for the advice and it seems worth trying. I don't know much above udev, will try it later when have time.

Chris said...

I will also try to work out something. Another interesting thing that I found on the web: http://code.google.com/p/wdleds/wiki/Project_Implementation

crumja said...

Or you can try sdparm --command-stop /dev/sdx

see: http://crumja.wordpress.com/2008/11/12/debian-blues/

Yan Li said...

To crumja:
You are right, command "sdparm --command-stop /dev/sdx" stops the drive, as listed as the 3rd step in the blog. But that alone is not enough before unconnecting the hard drive. The USB port should be put into "suspend" state.

According to Alan Stern, the best approach is "to send a SYNCHRONIZE CACHE command followed by START-STOP (if the device supports it), and then to disable or suspend the port. In Linux, those two commands will be sent automatically if you unbind the device from usb-storage. The suspend has to be done manually unless you have set up a udev rule (or something equivalent) to enable autosuspend for the device."

Gumper said...

I've had this same problem for sometime, I'm glad that I found your fix. One thing that I'm seeing is that after my drive is suspended, the power lights go out but when I shutdown my system with the usb drive still connected, the lights come back on. Do you have any ideas on how to correct this?

Yan Li said...

To Gumper:

That's interesting. What's the brand and model of your usb drive? Also have you tried it with other OSes (Mac/Win), do they have similar problem?

Chris said...

Let me tell you my findings with the script, too: after running it, it takes about 5-10 seconds before the light goes off and starts flashing in a 5 seconds intervall. That's it. To stop it completely I have to remove the power.

BTW: Yan Li, I wrote you a mail with a small change in newer versions of udev. I have no more udevinfo. Instead I have to use "udevadm info".

Gumper said...

My usb drive is the WD MyBook 500g model. On my machine I run Window XP and openSUSE 11.1. I do not see this problem with Windows. On windows the drive will power down completely (lights go off) when my machine is off. Your change does work, it spins down and suspends and the lights go out, until I power the machine down. Then they come back on. The only other way that I know how to correct it is to make a Kernal change, but the last two times I tried that on openSUSE didn't go well. Grub got messed up.

okos said...

I am having a little trouble with your script and consider myself pretty new to linux. I saved your script as wd_shutdown.
When I run the script root# ./wd_shutdown /dev/sda or sda1 it can not find my wd 1TB my book.
as root ./wd_shutdown /dev/sda
"./wd_shutdown: line 78: udevinfo: command not found
cannot find it's parent USB device,
perhaps it's not an USB device?"


fdisk -l shows the following
Device Boot Start End Blocks Id System
/dev/hdc1 * 1 5840 46909768+ 83 Linux
/dev/hdc2 5841 5948 867510 82 Linux swap
/dev/hdc3 5949 7296 10827810 83 Linux

Disk /dev/sda: 1000.2 GB, 1000204886016 bytes
255 heads, 63 sectors/track, 121601 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Disk identifier: 0x00073443

Device Boot Start End Blocks Id System
/dev/sda1 1 11917 95723271 c W95 FAT32 (LBA)
/dev/sda2 11918 37413 204796620 83 Linux
/dev/sda3 37414 108636 572098747+ f W95 Ext'd (LBA)
/dev/sda5 37414 62909 204796588+ c W95 FAT32 (LBA)
/dev/sda6 62910 75804 103579056 b W95 FAT32


lsusb shows:
Bus 002 Device 005: ID 1058:1102 Western Digital Technologies, Inc.
Bus 002 Device 003: ID 047d:1023 Kensington Pocket Mouse Pro Wireless
Bus 002 Device 002: ID 0451:2046 Texas Instruments, Inc. TUSB2046 Hub
Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 003 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 004 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Any suggestions?

Marcos said...

Hi Yan Li,
First of all, thank you for your script!
Just a remark... The routine to check if the device is mounted is not working here on my Ubuntu Gutsy. I was able to fix it by removing the extra space found in the grep expression ("^$DEV_NAME " changed to "^$DEV_NAME").
Also your script exits with a "Permission denied" error when it tries to suspend the USB port. I have figured out such error occurs because the "level" file was removed after unbinding the device. Is "unbind" really necessary? Why it can't be executed after suspending the USB port?
Thank you so much!
BR,
Marcos.

Chris said...

Another nice thing to have would be to make the script independent from USB. I have my device connected via Firewire and I think the same measures should be taken here.

Yan Li said...

To Gumper:

I tested my WD 1TB on a ThinkPad T61 laptop and hadn't found the problem you've described. The HD sat silently when the laptop was shutdown. So I can't help you in this case until I found a machine and can reproduce this problem.

You said you knew a way to correct this problem by changing the kernel, could you please share more on this? Thanks!

Yan Li said...

suspend-usb-device script updated: send STOP command before unbinding

Other minor enhancements:
1. output error msg to stderr
2. send STOP command before unbinding
3. warning on CONFIG_USB_SUSPEND not enable

Please get it from the link in the main article (or clone the git repo).

Anonymous said...

Yan Li, thank you very much for the script. It works very well.

Yan Li said...

suspend-usb-device script minor update: mount detection error fix

Rakesh Radhakrishnan said...

Hi Yan,

Thanks for the script. But there is a small problem with the script in my ubuntu system.

In my hardy system, support is there for USB suspend. /sys/bus/usb/devices/7-3/power/level is present also. But since after unbinding, this "level" file will be deleted.

So, when we are trying to suspend the drive after unbind, suspend will not work.


Is there any problem executing unbind after suspend. It is working fine. But in my opinion, it does not make sense to unbinding driver after suspending the device. What do you think?

Rakesh Radhakrishnan said...

forgot to mention my email id : krrakesh5@gmail.com

Jon said...

Many thanks for the script. I have been struggling to get spindown working on my Freecom Hard Drive 500Mb (internally its a SAMSUNG HD501LJ). Presently I am on Ubuntu Hardy Heron HP laptop, using on-board USB ports to the external drive.

And I had tried everything to get spindown to work - sg_start, scsi_stop, spindown, sdparm, hdparm, scsi-spin and eject. None of them actually got the device to spin down as it does if you remove the USB cable... and yet your script does exactly that. Thanks!

One interesting thing on my platform - the "unbind" part of your script *removes* the level file (and a few others from the power folder), thus tricking the last part of your script into thinking that it is not supported. However if I plug in the drive, umount any automatically mounts, I can do the suspend thing manually without problems.

Is it possible then that the unbind part of the process is unnecessary?

Thanks again, anyway - I will next be trying this on my NSLU2 ARM device :-)

Anonymous said...

i'm extremely pleased to have found someone who seems to have a solution to my problem. i cannot get ubuntu to effectively remove my wd passport before i unplug. as you said: the led is on and the drive is spinning. i've actually had my data corrupted, and i think improper removal is the problem. while i'm glad to have found your page, i am new to linux, and have no idea how to use your solution. if you could offer me some help, a step by step, on how to execute this, i'd really appreciate it, as i'm not sure how to use your script. thanks in advance.

Yan Li said...

Please feel free to contact me at: elliot (dot) li (dot) tech (at) gmail.com.

Yan Li said...

To Jon:
Thanks for your advice. As to my testing, both unbinding and suspending are necessary for fully bringing a device into proper mode for removing. Unbinding along won't tell the device to go into suspend mode, and for my WD Book 1T this won't shut the LED. And you shouldn't suspend a device without unbinding it since the cache may not be flushed correctly.

As to your case, if the power/level file is missing after unbinding, then I guess it's a bug of your kernel. Unbinding a device from a driver won't remove that device. If you could provide more information of your kernel version, distribution, etc., we might be able to dig into that issue.

Yan Li said...

suspend-usb-device script updated: Sync before sending SCSI STOP command, also added a new option to display device info only.

jla said...

Thank you so much for the post and program. I am so impressed with the last couple years improvements in the desktop experience and I hope to see it continue.

I think the suspend before 'safely remove' is an important element of this. I am much more confident my usb disk is ready to be unplugged when the power light has turned off or gone dim as it does when I 'safely remove' in MS Windows.

I hope to see a standard mature version on every distro like the eject command.

Gumper said...

Hi Yan,

Sorry I didn't answer your question earlier. The following is the change that I make to the kernel to get the USB port to shut down completely when the machine shuts down.

http://launchpadlibrarian.net/9603948/linux-2.6.22-usb-shutdown.diff

I'll try to check back to see if you have any other questions for me.

Eric said...

Can I replace sdparm with hdparm?

Instead of:
sdparm --command=sync $DEV_NAME"
sdparm --command=stop "$DEV_NAME"

How about this:
hdparm -f "$DEV_NAME"
hdparm -Y "$DEV_NAME"

mgrimes said...

Great post/script, thanks. Once tweak, I had to change it udevinfo to 'udevadm info' on my Arch Linux system.

-Mark

Yan Li said...

To Marcos:
> Just a remark... The routine to check if the device is mounted is
> not working here on my Ubuntu Gutsy. I was able to fix it by
> removing the extra space found in the grep expression ("^$DEV_NAME "
> changed to "^$DEV_NAME").

This is fixed in the script. Please update. Thanks for pointing this out.

> Also your script exits with a "Permission denied" error when it
> tries to suspend the USB port. I have figured out such error occurs
> because the "level" file was removed after unbinding the device. Is
> "unbind" really necessary? Why it can't be executed after suspending
> the USB port?

Jon reported a similar issue. Please refer to my reply to Jon. Basically I think it might be a bug in your running kernel. I'm using latest stable kernel release from upstream (2.6.29.1 now) and I haven't seen such issue.

Yan Li said...

suspend-usb-device script updated: use 'udevadm info' instead of obsoleted udevinfo

Thanks for those friends that have pointed this out. N.B. this change might break old systems that still running udevinfo. If you saw that problem, please let me know.

Yan Li said...

To Chris, I don't have a firewire device for testing. If you can add support for that and send me a patch I can merge it, thanks!

Yan Li said...

To Rakesh Radhakrishnan:
> In my hardy system, support is there for USB
> suspend. /sys/bus/usb/devices/7-3/power/level is present also. But
> since after unbinding, this "level" file will be deleted.

Other people have met similar issues. But since I haven't observed this issue in latest stable kernel release (till 2.6.29.1) nor 2.6.26 Debian Lenny kernel, it might be a bug of the kernel used by Hardy.

Unfortunately I have no Hardy system for testing now (all people around me are running Intrepid or Jaunty). Could you please try a latest upstream kernel by using KernelMainlineBuilds? And if the new kernel is OK, filing a bug against Hardy's kernel?

> Is there any problem executing unbind after suspend. It is working
> fine. But in my opinion, it does not make sense to unbinding driver
> after suspending the device. What do you think?

You have to unbind the device before putting it into suspend mode since unbinding flush the cache. Directly putting a device into suspend mode without flushing cache may lead to data corruption.

Chris said...

Does anyone know how I could use udev to start the script? The problem is that a "ACTION==remove" in a udev rule is only triggered when the device is removed (i.e. disconnected) from the computer, and that would be too late for the script to do it's work.

Yan Li said...

To Chris: Since the operating system must be told that you wish to remove an USB device, this event must be initiated from a GUI tool. Though I'm not an GNOME nor KDE expert so I'm not sure what is the best way to implement this.

Yan Li said...

To Eric:
> Can I replace sdparm with hdparm?

Sorry that I'm not sure about this. Broader testing with a wide range of devices is necessary to determine this.

Chris said...

Yan Li, can you please tell me where to send patches for the script to? I already mailed you some time ago but did not receive an answer.

Yan Li said...

To Chris: sorry, I must have missed your mail. You can reach me via "yanli {AT} infradead {DOT} org". Thanks!

d.tonhofer@m-plify.com said...

Very nice and more or less what I was looking for. Having an encrypted filesystem on my USB disk makes me want to be sure that I have safely powered it down before disconnecting it.

However, I encountered a problem in that the script switches off more than it should. In particular, I had the disk hanging off a USB hub (a Logitech G11 keyboard -- using an external power source for the disk is mandatory in that case btw). Running the script then powered-down the keyboard as well as the mouse and the disk connected to it. Not good.

Similarly, when targeting a disk connected to the main chassis, running the script powered off the USB connector for good; there seemed to be no way to get it working again except rebooting.

I think the script should look for a USB device "deeper down" in the USB device tree. I have modified it somewhat, making it call a Perl program that parses "udevadm" output such that the parent device of the device with DRIVER=="usb-storage" and SUSBYS=="usb" is targeted. This is "deeper down" than the device targeted by the original script. Which seems to work.

Additional minor changes are: added a "verbose" flag, moved the test for a zero $DEVICE upwards, and fixed the list of options in getopts.

The modified script can be obtained from my employer's site:

http://public.m-plify.net/suspend_disk/suspend-usb-device.sh

The Perl script from:

http://public.m-plify.net/suspend_disk/usb_device_filter.pl

One may have to correct the FILTER variable in the shell script to make it point to the perl script.

Best regards,

-- David

pollanza said...

Hello, I've tried to make your scrip work on my WD Passport 2.5" 250gb, but it seems not to work... After launching the script, the drive's light started to flash really quick, like it does when it's reading or writing files! It was also spinning with its usual vibration!
Is there something I can do to make it spin down the drive?
Thanks a lot!

Yan Li said...

To pollanza: that's weird, I have never seen that. Could you please try using another USB port? And could you please run that script with "bash -x ./suspend-usb-device" (this just turns on debugging output) and tell me what is displayed when your drive's light is flashing abnormally?

Yan Li said...

To David: Oh! Thanks for the script. Let me try it.

pollanza said...

I've modified your script with a suggestion from a friend of eeepc.it forum and now it works! Now it spins down the plate of the drive!!
Here there is the modification:
# send SCSI sync command, some devices don't support this so we just
# ignore errors
#sdparm --command=sync "$DEV_NAME" >/dev/null || true
hdparm -f "$DEV_NAME" >/dev/null || true
# send SCSI stop command
#sdparm --command=stop "$DEV_NAME" >/dev/null
hdparm -Y "$DEV_NAME" >/dev/null

Thanks a lot for the very usefull script!

Yan Li said...

To d.tonhofer@m-plify.com: I've carefully studied your script (usb_device_filter.pl). It was very well-written. Thank you. I'm thinking that we can use a simpler way to get the correct parent of the usb-storage device. In a devpath, it's always the one two slashes before "host*", eg. for a devpath like:

/devices/pci0000:00/0000:00:1d.0/usb5/5-2/5-2.2/5-2.2:1.0/host4/target4:0:0/4:0:0:0

what we want is two slashes before host4, hence 5-2.2 here.

I've tested this way and it worked well with my USB hubs. By this way the modification to current script is trivial and no need to use another Perl script, which would bring in more dependencies. However I'm not sure whether it works in all situations so it would be kind of you to test the latest script as you can find in the git repo.

Yan Li said...

suspend-usb-device script updated: per David's advice this script now works better with USB hubs, only the designated device will be shutdown instead of the whole USB hub. Also includes Christian Schmitt's patch for supporting Firewire devices (need more testing). Please write to me or leave comments if you saw any bugs. Thank you.

ssergeje said...

hi and thanks for the script.

I use it on ubuntu 9.04 with WD drive. It works for the first unplug.

When after some time I connect the drive again and want to detach it for a second time, then the script does what it should (sync, stop) but the drive continues running. Removing the wires gives that ugly forced HDD shutdown noise.

There's a weak discussion on LP https://bugs.launchpad.net/ubuntu/+bug/117713 with no working solution

Have you experienced this?

Alex Samorukov said...

Thank you very much for your article and script. I`m using it with minor changes (umount all active filesystem) for my USB HDD drive on ubuntu.

Evaldas said...

Hi, I've got quite a problem. Ant I'm quite new to linux. Your script works fine, at least for a second. It does succesfuly power off my hard drive, then immediately it powers on, but it doesnt mount. /dev/sdb is not created. It seems system doesn't seem to be knowing that something is plugged at all..

d.tonhofer@m-plify.com said...

Hi Yan Li, David here.

I have tested the new script, which removes the need for the additional perl script with a pipe. Thumbs up.

I have encountered some problems though. However, these have nothing to do with the script itself.

On "Fedora 10" (ASUS motherboard), things work quite well. An USB disk with an external power supply can be powered off. Replugging it powers it back on, as expected.

The device tree:

/devices/pci0000:00 => :
/devices/pci0000:00/0000:00:1d.7 => ehci_hcd:pci
/devices/pci0000:00/0000:00:1d.7/usb1 => usb:usb
/devices/pci0000:00/0000:00:1d.7/usb1/1-7 => usb:usb
/devices/pci0000:00/0000:00:1d.7/usb1/1-7/1-7.2 => usb:usb ***** POWER THIS ONE OFF *****
/devices/pci0000:00/0000:00:1d.7/usb1/1-7/1-7.2/1-7.2:1.0 => usb-storage:usb
/devices/pci0000:00/0000:00:1d.7/usb1/1-7/1-7.2/1-7.2:1.0/host13 => :scsi
/devices/pci0000:00/0000:00:1d.7/usb1/1-7/1-7.2/1-7.2:1.0/host13/target13:0:0 => :scsi
/devices/pci0000:00/0000:00:1d.7/usb1/1-7/1-7.2/1-7.2:1.0/host13/target13:0:0/13:0:0:0 => sd:scsi
/devices/pci0000:00/0000:00:1d.7/usb1/1-7/1-7.2/1-7.2:1.0/host13/target13:0:0/13:0:0:0/block => :
/devices/pci0000:00/0000:00:1d.7/usb1/1-7/1-7.2/1-7.2:1.0/host13/target13:0:0/13:0:0:0/block/sdb => :block

On "Fedora 11" (Fujitsu-Siemens laptop), things go wrong. The same USB disk is not powered off reliably (sometimes it is powered off and sometimes it isn't). It also sometimes goes into a state where it doesn't become visible to the system after disconnect/reconnect. This may just be a problem with the laptop hardware (which is cheap and nasty), or maybe there is something wrong in the OS. I am building another F11 system and may be able to test on that one soon.

The device tree:

/devices/pci0000:00 => :
/devices/pci0000:00/0000:00:13.2 => ehci_hcd:pci
/devices/pci0000:00/0000:00:13.2/usb1 => usb:usb
/devices/pci0000:00/0000:00:13.2/usb1/1-4 => usb:usb ***** POWER THIS ONE OFF *****
/devices/pci0000:00/0000:00:13.2/usb1/1-4/1-4:1.0 => usb-storage:usb
/devices/pci0000:00/0000:00:13.2/usb1/1-4/1-4:1.0/host2 => :scsi
/devices/pci0000:00/0000:00:13.2/usb1/1-4/1-4:1.0/host2/target2:0:0 => :scsi
/devices/pci0000:00/0000:00:13.2/usb1/1-4/1-4:1.0/host2/target2:0:0/2:0:0:0 => sd:scsi
/devices/pci0000:00/0000:00:13.2/usb1/1-4/1-4:1.0/host2/target2:0:0/2:0:0:0/block/sdb => :block

ssergeje said...

Yan, I've updated your script and now it both unmounts the drive and works under Ubuntu 9.04
Refer to http://ssergeje.wordpress.com/2009/08/11/unmounting-external-usb-drives-in-ubuntu/

Yan Li said...

Hi David, I haven't met an issue similar to yours on any machine so I can't debug. I'll report if I met it. Thank you.

Yan Li said...

To Evaldas: hum... people are reporting similar issues and those problems seem to be machine-specific. I haven't met similar issues here so I can't debug. David is trying and hope he could find the root cause for this.

cisco said...

Great blog and nice post I am trying to maintain a list of cute blogs. Thanks and nice collection of blogs. I’m going to have to browse through those. it’s nice I can come and read your blog.
ccna ccent

Flash Drives said...

Thanks you guys for suggesting such the nice solutions. Thanks again.

Character Education said...

I haven't used the Linux operating system till now but have the plan to use it for Education and training purpose in the future, Thanks for the Education regarding the useful thing. keep it up.

Breitling Replica Watches said...

I am just planning to use the Linux Operating system, Windows provide a easy way to remove the USB Drive, Is there any complexity in the Linux.

Anonymous said...

you have a nice site. thanks for sharing this enormous resources. keep it up. anyway, various kinds of ebooks are available here

http://feboook.blogspot.com

Tom said...

Hi Yan Li,

Great work on this problem, but as you anticipated some of us are still stuck with udevinfo...

Could you post the old version of the appropriate line? I've tried to revert the udevadm command to udevinfo myself but can't get it to work..

Cheers,
Tom

lolc said...

Hi Yan Li,

Thanks for the wonderful script.

However, for my Western Digital USB harddisk, I need to add the following line before sending "sdparm --command=sync"

eject "$DEV_NAME"

Else, the harddisk will not spin down.

Gateway Hard Drive Repair said...

Being a Gateway Laptop Hard Drive repair I have never encountered with such a system where I had to remove an USB Hard drive in Linux. You have explained it well and certainly it will not only help me but it will be useful to million people out there.

Bud said...

Thank you for the script it's very useful.

In kernel 2.6.33 there is a new "remove hardware" sysfs attribute, which could be used in the script somehow.

BlackFateGR said...

With the new kernel 2.6.33 echo "suspend" wont work any more. According to kernel power management documentation, suspend is only for kernels 2.6.32 and lower and it was replaced by "auto". Setting to auto the device will autosuspend after 2 secs. This can be also be changed by setting autosuspend file value to 0

leva said...

What a wonderful script! Thanks. Now I wonder how to wake up the device back after it was suspended?

catkin said...

Many thanks for the script Yan Li and to everyone else who has contributed but maybe some USB HDDs just don't behave!

I tried with a Hitachi SimpleDrive mini, model HTS545050B9A300, using the latest version of the script from the git (enhancement request: a version number in the script) and with a kernel with CONFIG_USB_SUSPEND enabled.

No error messages but the HDD went right on spinning. I also tried quiescing it with sdparm first. Here's the last attempt:

root@CW8:~# sdparm --command=sync /dev/sdc
/dev/sdc: Hitachi HTS545050B9A300
root@CW8:~# sdparm --command=stop /dev/sdc
/dev/sdc: Hitachi HTS545050B9A300
root@CW8:~# sdparm --command=eject /dev/sdc
/dev/sdc: Hitachi HTS545050B9A300
root@CW8:~# /home/c/d/bin/try/suspend-usb-device.sh -v /dev/sdc
Found device /devices/pci0000:00/0000:00:13.5/usb1/1-5 associated to /dev/sdc; USB bus id is 1-5
Syncing device /dev/sdc
Stopping device /dev/sdc
Unbinding device 1-5
Checking whether /devices/pci0000:00/0000:00:13.5/usb1/1-5 can be suspended
Suspending /devices/pci0000:00/0000:00:13.5/usb1/1-5 by writing to /sys/devices/pci0000:00/0000:00:13.5/usb1/1-5/power/level

In case it matters, here are the associated /sys/devices power directories:

root@CW8:~# find /sys/devices -type d -iname power | grep '1-5'
/sys/devices/pci0000:00/0000:00:13.5/usb1/1-5/power
/sys/devices/pci0000:00/0000:00:13.5/usb1/1-5/1-5:1.0/power
/sys/devices/pci0000:00/0000:00:13.5/usb1/1-5/1-5:1.0/host4/power
/sys/devices/pci0000:00/0000:00:13.5/usb1/1-5/1-5:1.0/host4/scsi_host/host4/power
/sys/devices/pci0000:00/0000:00:13.5/usb1/1-5/1-5:1.0/host4/target4:0:0/power
/sys/devices/pci0000:00/0000:00:13.5/usb1/1-5/1-5:1.0/host4/target4:0:0/4:0:0:0/power
/sys/devices/pci0000:00/0000:00:13.5/usb1/1-5/1-5:1.0/host4/target4:0:0/4:0:0:0/scsi_disk/4:0:0:0/power
/sys/devices/pci0000:00/0000:00:13.5/usb1/1-5/1-5:1.0/host4/target4:0:0/4:0:0:0/block/sdc/power
/sys/devices/pci0000:00/0000:00:13.5/usb1/1-5/1-5:1.0/host4/target4:0:0/4:0:0:0/block/sdc/sdc1/power
/sys/devices/pci0000:00/0000:00:13.5/usb1/1-5/1-5:1.0/host4/target4:0:0/4:0:0:0/scsi_device/4:0:0:0/power
/sys/devices/pci0000:00/0000:00:13.5/usb1/1-5/1-5:1.0/host4/target4:0:0/4:0:0:0/scsi_generic/sg3/power
/sys/devices/pci0000:00/0000:00:13.5/usb1/1-5/1-5:1.0/host4/target4:0:0/4:0:0:0/bsg/4:0:0:0/power
/sys/devices/pci0000:00/0000:00:13.5/usb1/1-5/1-5:1.0/usb_endpoint/usbdev1.4_ep81/power
/sys/devices/pci0000:00/0000:00:13.5/usb1/1-5/1-5:1.0/usb_endpoint/usbdev1.4_ep02/power
/sys/devices/pci0000:00/0000:00:13.5/usb1/1-5/usb_device/usbdev1.4/power
/sys/devices/pci0000:00/0000:00:13.5/usb1/1-5/usb_endpoint/usbdev1.4_ep00/power

Kirikaza said...

David's idea to get the parent by DRIVER=="usb-storage" and SUBSYS=="usb" is not "a simpler way" but it is more correct one. If you do not check DRIVER or/and SUBSYS you will do some bad things with SATA devices. What is /dev/sdd? Is it a USB-device or a hot-plugged SATA-device? I think you would better filter devices by asking udevadm.

catkin said...

It may be prudent to explicitly set the $PATH in the script. On Slackware 13.0 32-bit running udevd 141, when a bash script is run from udev rules the PATH has bash' default, "/usr/gnu/bin:/usr/local/bin:/bin:/usr/bin:.", presumably because udevd does not provide a PATH environment variable. A good choice might be "/usr/sbin:/sbin:/usr/bin:/bin".

jengelh said...

There is not really a need to use hdparm. sdparm covers all that's done through sd (including, but not limited to, SCSI, USB, IEEE1394, SATA and PATA).

iaindb said...

Hi, great script! I discovered it after looking into sdparm myself. I found that --command=stop didn't spin down my USB disk.

When I run your script I get ./suspend-usb-device: line 180: echo: write error: Invalid argument

This comes from the last command: echo 'suspend' > "$POWER_LEVEL_FILE". If I find the level file and try to echo to it myself (as root) I get the same error. I have CONFIG_USB_SUSPEND built into my kernel.

The disk is an Hitachi HTS545016B9A300 in an Astone USB/eSATA external case (I'm using the USB port since I don't have eSATA on my laptop). The case is listed as:
152d:2339 JMicron Technology Corp. / JMicron USA Technology Corp.

The device does disappear from /dev though.

Any ideas? Thanks :)

Canada flower shops said...

I suggest this site to my friends so it could be useful & informative for them also. Great effort.

Enrique said...

Nice to see that somebody is concerned with this issue. I've been using Linux during 6 years and I've got 3 HD (1 external and 2 internal) broken. They've always shut down while still spinning, making an annoying noise.

The script doesn't work with an iomega USB-2.5"HD. However, after (only!) unmounting the disk, the following seems to spin it down (only LED light is kept):
$ sudo hdparm -Y /dev/sdc1
/dev/sdc1:
issuing sleep command
HDIO_DRIVE_CMD(sleep) failed: Invalid exchange
where /dev/sdc1 is the USB-HD device.

Is it safe? If note, How should I adapt your script to support my disk too, so the other/missing steps are done?
or, maybe, is there any cleaner and automatic solution (probably with udev rules)?

Thanks

Yan Li said...

@Enrique, I'm sorry that I don't have an iomega USB-2.5"HD to test and your problem seems device-specific. As to my knowledge I think "hdparm -Y" is safe (but no guarantee).

You can edit my script and replace the "sdparm --command=stop" line with the "hdparm -Y" and try it with your HD.

Good luck!

Enrique said...

I have tried to adapt your script to my iomega USB-HD. But something really weird is happening: the hdparm -Y instruction is very slow when it is run from the script. I don't know why.

Thus, I've been playing with the following (you already know the full commands):
- sdparm-sync
- sdparm-stop
- hdparm-y
- unbind

From the command line, the following seems to work:
1. sdparm-sync
2. sdparm-stop
3. hdparm-y (spindown)
4. sdparm-stop again to power down (switch led off)
5. unbind
If step 2 is not done, it doesn't work.
If step 5 is not done, after a few (~10) seconds the disc is resumed (spinning again).

Unfortunately, if I adapt the script to do the 5 steps above, it doesn't work: hdparm-y takes so much time and the disc is resumed. Any idea, why the script behaves differently?

Yan Li said...

@Enrique: an obviously difference between running commands from command-prompt and within a script is the pause between commands. Perhaps you can try to add a few "sleep 2" between the commands in the script. Also I'd suggest remove "2. sdparm-stop" and try again.

Enrique said...

@Yan Li: You are completely right, I had to put some sleep commands, and I only need one sdparm-stop.

At the end, taken your original script and reducing the number of sleep commands, this is the only change needed (between sdparm-sync and sdparm-stop):
# send HD sleep command
[[ $VERBOSE == 1 ]] && echo "Sleeping device $DEV_NAME"
sleep 1
hdparm -Y "$DEV_NAME" >/dev/null 2>/dev/null || true

There's still one little problem: the led doesn't switch off. I'm not sure why, because sometimes it does. Anyway, being on or off, later when the device is unbound, the led get switched on again.
Not a big problem, I think? But, the usb link is not in suspend state then, isn't it?

I'll like to make this automatic when I press unmount or remove-safely. Do you know if this can be done with udev rules? (I've been looking here: http://reactivated.net/writing_udev_rules.html#external-run)

Thanks for your comments!

Chris said...

That's a very useful script, thanks!

Incidentally, is it possible to reverse the process, i.e. power the USB drive back on and have it reconnect? The reason is that I want to use USB drives for backup, and don't want them running when not doing backup (ideally I'd have one drive per day used in rotation, the way we used to do with tapes), and want them to be powered on by software. At the moment I do this with a timer switch, kill the power to the drive for 15 minutes once a day and let your script power it off after backup, but that's rather inelegant.

promotional usb drives said...

This is brilliant, thanks for taking the time to write this post. I've struggled with this problem for quite a while until coming across this post. It's quite easy to follow your steps and I haven't come across any problems, thanks again.

kr said...

Hi Yan Li,

I found your explanations useful, but the script is not working as expected for me (or is it my drive ?).

I'm using a 2.6.36 kernel and, as BlackFateGR said, I had an error with this line (at the end) :
echo 'suspend' > "$POWER_LEVEL_FILE"
I replaced the 'suspend' by 'auto' (as suggested) and the error message disappeared...

...but the drive is still spinning, with the lights off !

It's a Seagate GoFlex. Is it safe to remove a spinning drive ?

Yan Li said...

@kr it's hard to say. Perhaps you can try the driver on an officially supported OS (hint: Windows) and see whether it keeps spinning after being removed from the OS. If it spins similarly then I speculate it should be safe to be removed then.

Actually after GNOME has built-in "Safe remove" function this script is no longer useful anymore. Moreover, I heard it doesn't work with latest kernels due to changes in the USB power management knob.

kr said...

Thank you for your answer.

Yes, it's the same behavior under Windows.
I expected a way to manually and completely power it down.
If it's no longer supported, I will do without.

BlackFateGR said...

Well actually, Windows 7 dont power off my wd passport anymore, while ubuntu and other udisk based distros do. So just use gnome disk utility to make that possible for your distro

Review Daddy said...

Thanks

mens boots said...

Dude Thanks for this,demonstrating how to do this help me a lot.

Anonymous said...

Yan Li, I've used your script countless times but I've never got the time to Thank you!

Actually the stupid ubuntu/gnome does the same with safely remove, but AFTER A FEW SECONDS IT SPINS UP THE DRIVE AGAIN!!!

Your script puts the drive for good in the powerdown mode!!!

I had however recently the need to perform it on a centos 5 which does not have udevadm so I had to modify this line to make it work in centos 5:

DEVICE=$(udevinfo -q path -n ${DEV_NAME} -a -p /sys/block/`echo ${DEV_NAME}|sed 's/\/dev\///'`/device/ | \

Yan Li said...

I'm happy that this old script is still useful to the world.

Maybe you should report the Ubuntu/GNOME bug to them (that sounds serious to me). Personally I'm using various versions of Debian on many machines and I haven't observed such an issue so maybe it's Ubuntu-specific. Actually it's because I feel the GNOME is doing well enough on this little task so I don't have to maintain my ad hoc script any more.

a.r.hlayhel said...

interesting, but shouldn't there be a simple way like in windows.
using the safe remove option in the disk manager after unmounting the external drive will stop the drive and save you the extra work

Anonymous said...

I'm not sure but... udisks --detach /dev/sdx

Anonymous said...

The answer to the users that report "Permission denied" is to go into a root shell by typing: sudo -i
then you can just write the echo ... > ... commands

ashraaf muhammed said...

good blog really,please can you tell me your opinion in my blog

http://flashdrive-repair.blogspot.com

Anonymous said...

I've got error bad argument while echo 'suspend' ...
This was because commands have changed in new kernel.

Solution found @ https://bbs.archlinux.org/viewtopic.php?id=95659

echo "0" > "/sys/bus/usb/devices/$port/power/autosuspend"
echo "auto" > "/sys/bus/usb/devices/$port/power/level"

About Me

My Photo
Santa Cruz, California, United States