[ltp] handling UltraBay with hard disk

Peter F. Patel-Schneider linux-thinkpad@linux-thinkpad.org
Tue, 05 May 2009 15:13:06 -0400 (EDT)


From: Peter F. Patel-Schneider <pfps@research.bell-labs.com>
Subject: Re: [ltp] handling UltraBay with hard disk
Date: Tue, 05 May 2009 07:17:29 -0400 (EDT)

[...]

> Lay it on me.  What I have is a modified version of the hotswap stuff
> from ThinkWiki and *it* is a crazy combination of a shell script and
> perl.  The ThinkWiki script unmounts, powers off the device, deletes the
> device, and then powers off the UltraBay itself, but unfortunately
> doesn't quite work in F11.  What I do have working is unmounting and
> then deleting the SCSI device.
> 
> What I would like to have would be a general script that in a safe and
> sane and *general* fashion does everything needed, for any dock/bay
> device that has a disk in it (maybe even including a CD).  However, I
> don't see how to really correctly do that in a udev rule, particularly
> coordinating the actions on the device (unmount, ...) and the bay (power
> down).
> 
> Perhaps I'm just stuck with my 90% solution (upgraded to a 95%
> solution).
> 
> I would also like to update the ThinkWiki page
> http://www.thinkwiki.org/wiki/How_to_hotswap_UltraBay_devices
> but I don't know what can safely be removed.  Perhaps I'll just clean
> out anything that looks too old to me and see if anyone screams.
> 
> peter

As thinkwiki.org has been down for a while, I thought that I would
socialize my 90% solution directly.

My solution has the following problems:
1/ The code to get from the disk to the mounts is too complex.
2/ I can't get the "spin hard drive down" to work.
3/ The link from the disk to the bay is hard coded so the script is
   not general.

However, the following scenarios all work and end up only four
different states 
- A - bay in and disk/bay on - eject tab in
- B - bay in and disk/bay off - eject tab out
- C - bay in and disk/bay on - eject tab out - error state
- D - bay out

1/ Unmount (from state A)
- flip the switch
- unmount is successful and bay turned off
- ends up in state B

2/ Remove (from state B)
- pull the bay out, using the eject tab
- ends up in state D

3/ Insert (from state D)
- push the bay in 
- ends up in state B

4/ Mount (from state B)
- push the eject tab in
- ends up in state A

5/ Drive busy (from state A)
- flip the switch
- drive is busy so unmount is not successful and bay not turned off
- goes to state C
- pushing eject tab in goes back to state A


What I did was add a udev rule

ENV{BAY_EVENT}=="3", ACTION=="change", SUBSYSTEM=="scsi", RUN+="/usr/local/sbin/ultrabay_eject"

with the following script, modified from 
http://www.thinkwiki.org/wiki/How_to_hotswap_UltraBay_devices

#!/bin/bash
# Semi-graceful unmount of the SCSI device in a ThinkPad Ultrabay 
# in response to flipping the pre-eject switch

ULTRABAY_SYSDIR=/sys$DEVPATH
shopt -s nullglob
logger ultrabay_eject unmounting storage device $DEVPATH

# Umount the filesystem(s) backed by the given major:minor device(s)
## It would be nice to do this in the shell instead
unmount_rdev() { perl - "$@" <<'EOPERL'  # let's do it in Perl
	for $major_minor (@ARGV) {
		$major_minor =~ m/^(\d+):(\d+)$/ or die;
		push(@tgt_rdevs, ($1<<8)|$2);
	}
        # Sort by reverse length of mount point, to unmount sub-directories first
        open MOUNTS,"</proc/mounts" or die "$!";
        @mounts=sort { length($b->[1]) <=> length($a->[1]) } map { [ split ] } <MOUNTS>;
        close MOUNTS;
        foreach $m (@mounts) {
                ($dev,$dir)=@$m;
		next unless -b $dev;  $rdev=(stat($dev))[6];
		next unless grep($_==$rdev, @tgt_rdevs);
		system("logger","ultrabay_eject","umounting",$dev,$dir);
		system("umount","-v","$dir")==0  or  $bad=1;
	}
	exit 1 if $bad;
EOPERL
}

## This doesn't seem to work somehow - I don't know why
ultrabay_dev_node() {
	shopt -u nullglob
	DEV_SYSPATH="$@"
	UDEV_NAME=`udevadm info --query=name --path=$DEV_SYSPATH` || return 1
	echo /dev/$UDEV_NAME
}

if [ -d $ULTRABAY_SYSDIR ]; then
	sync
	# Unmount filesystems backed by this device
	## This seems to be very inelegant and prone to failure
	unmount_rdev `cat $ULTRABAY_SYSDIR/block/*/dev     \
	                  $ULTRABAY_SYSDIR/block/*/*/dev`  \
	|| {
		logger ultrabay_eject umounting failed
		echo 2 > /proc/acpi/ibm/beep  # error beep
		exit 1
	}
        sync
        ## Nicely power off the device ??  - Can't get this to work!
#	DEVNODE=`ultrabay_dev_node $ULTRABAY_SYSDIR/block/*` && hdparm -Y $DEVNODE
        # Let HAL+KDE notice the unmount and let the disk spin down

	# Unregister this SCSI device:
	sync
	logger ultrabay_eject deleting device
	echo 1 > $ULTRABAY_SYSDIR/delete
	# Need to sleep to let something disassociate the drive from the dock
	sleep 0.5
else
	logger ultrabay_eject no ultrabay device directory
	echo 2 > /proc/acpi/ibm/beep  # error beep
	exit 1
fi
sync

# Turn off power to the UltraBay:
## This is fragile and obviously the wrong way to go, 
## but I don't know how to go from the disk to the bay
if [ -d /sys/devices/platform/dock.2 ]; then
	logger ultrabay_eject undocking bay
	echo 1 > /sys/devices/platform/dock.2/undock
fi
# Tell the user we're OK
logger ultrabay_eject done
echo 12 > /proc/acpi/ibm/beep