Subject: [PATCH] [FEAT LS1215] dasd: add safe offline interface
From: Stefan Haberland <stefan.haberland@de.ibm.com>

Summary:     dasd: add safe offline interface
Description: The regular behavior of the DASD device driver when setting a
             device offline is to return all outstanding I/O as failed. This
             behavior is different from that of other System z operating
             systems and may lead to unexpected data loss. Adding an explicit
             'safe' offline function will allow customers to use DASDs in the 
             way they expect them to work.
Upstream-ID: -
Problem-ID:  LS1215

Signed-off-by: Stefan Haberland <stefan.haberland@de.ibm.com>
---
 zconf/chccwdev   |   92 ++++++++++++++++++++++++++++++++++++++-----------------
 zconf/chccwdev.8 |   32 ++++++++++++++-----
 2 files changed, 88 insertions(+), 36 deletions(-)

--- a/zconf/chccwdev
+++ b/zconf/chccwdev
@@ -29,6 +29,8 @@
 CMD=$(basename $0)
 MAX_RETRIES=5
 CIO_SETTLE="/proc/cio_settle"
+ONLINEATTR="online"
+SYSPATH=NULL
 
 if [ "$(cat /proc/filesystems|grep sysfs)" = "" ]; then
 	echo "ERROR: $CMD requires sysfs support!" >&2
@@ -52,6 +54,8 @@ function PrintUsage() {
 		 		driver supports this.
 		  	-d|--offline
 		  		Tries to set the given device offline.
+		  	-s|--safeoffline
+		  		Tries to set the given device offline waiting for all outstanding I/O. May block forever.
 		 	-v|--version
 		 		Show tools and command version.
 
@@ -68,6 +72,15 @@ function PrintVersion()
 	EOD
 }
 
+function CheckOnlineArg()
+{
+    if [ "$ONLINE" = $1 ] ;then
+	echo "$CMD: Incompatible argument list." >&2
+	echo "Try '$CMD --help' for more information." >&2
+	exit 1
+    fi
+}
+
 function SetAttribute()
 {
 	local NAME="$1"
@@ -81,15 +94,24 @@ function SetAttribute()
 	fi
 	if [ "$NAME" = "online" ]; then
 		if [ "$VAL" = "force" ]; then
+			CheckOnlineArg 0
 			ONLINE=1
 			FORCE="force"
 		elif [ "$VAL" = "1" ]; then
+			CheckOnlineArg 0
 			ONLINE=1
 		else
+			CheckOnlineArg 1
 			ONLINE=0
 		fi
 		ACTIONSET=true
 		return
+	elif [ "$NAME" = "save_offline" ]; then
+		CheckOnlineArg 1
+		ONLINE=0
+		ACTIONSET=true
+		ONLINEATTR="safe_offline"
+		return
 	fi
 
 	while [ $CNT -lt $NUMATTR ]; do
@@ -128,26 +150,25 @@ while [ $# -gt 0 ]; do
 				exit 1
 				;;
 		-e|--online)
-				ONLINE=1
-				ACTIONSET=true
+				SetAttribute "online" 1
 				;;
 		-f|--forceonline)
-				ONLINE=1
-				FORCE=force
-				ACTIONSET=true
+				SetAttribute "online" "force"
 				;;
 		-d|--offline)
-				ONLINE=0
-				ACTIONSET=true
+				SetAttribute "online" 0
+				;;
+		-s|--safeoffline)
+				SetAttribute "save_offline" 0
 				;;
 		-v|--version)
 				PrintVersion
 				exit 0
 				;;
 		-*)
-				echo "$CMD: Invalid option $1"
-				echo "Try 'chccwdev --help' for more" \
-					"information."
+				echo "$CMD: Invalid option $1" >&2
+				echo "Try '$CMD --help' for more" \
+				    "information." >&2
 				exit 1
 				;;
 		*)
@@ -203,6 +224,10 @@ BUSIDLIST=$(
 		return 1
 	}
 	function ExpandBusID(id) {
+		if (length(id) < 1) {
+			print "\"\" is not a valid bus ID." >err
+			return ""
+		}
 		split(id, part, ".")
 		if (3 in part) {
 			css = part[1]
@@ -359,6 +384,27 @@ function StoreAttribute()
 	return 0
 }
 
+function PrintError()
+{
+	read ERROR
+	if [ -n "$ERROR" ] ;then
+		echo "Failed (${ERROR##*: })" >&2
+		if [ ! -e $SYSPATH/driver ]; then
+			read CUTYPE 2>/dev/null < $SYSPATH/cutype
+			read DEVTYPE 2>/dev/null < $SYSPATH/devtype
+			if [ $? -ne 0 ] ;then
+				exit 1
+			fi
+			if [[ $DEVTYPE == "n/a" ]] ;then
+				DEVTYPE="0000/00"
+			fi
+			echo "Note: No driver is attached to this device" \
+				"(DevType:$DEVTYPE CU Type:$CUTYPE)." >&2
+		fi
+		exit 1
+	fi
+}
+
 SAVEDATTRS=("${ATTRVAL[@]}")
 for BUSID in $BUSIDLIST; do
 	SYSPATH=$SYSFSDIR/bus/ccw/devices/$BUSID
@@ -395,27 +441,17 @@ for BUSID in $BUSIDLIST; do
 			else
 				echo "Setting device $BUSID offline"
 			fi
+			if [ ! -e $SYSPATH/$ONLINEATTR ]; then
+				echo "$ONLINEATTR attribute not available for" \
+				    " device[$BUSID]" >&2
+				exit 1
+			fi
 			if [ "$FORCE" != "" ]; then
-				ERROR=$(echo $FORCE 2>&1 >$SYSPATH/online)
+				echo $FORCE 2>&1 >$SYSPATH/$ONLINEATTR | PrintError
 			else
-				ERROR=$(echo $ONLINE 2>&1 >$SYSPATH/online)
-			fi
-			if [ $? -ne 0 ] ;then
-				echo "Failed (${ERROR##*: })" >&2
-				if [ ! -e $SYSPATH/driver ]; then
-					read CUTYPE 2>/dev/null < $SYSPATH/cutype
-					read DEVTYPE 2>/dev/null < $SYSPATH/devtype
-					if [ $? -ne 0 ] ;then
-						exit 1
-					fi
-					if [[ $DEVTYPE == "n/a" ]] ;then
-						DEVTYPE="0000/00"
-					fi
-					echo "Note: No driver is attached to this device" \
-					     "(DevType:$DEVTYPE CU Type:$CUTYPE)." >&2
-				fi
-				exit 1
+				echo $ONLINE 2>&1 > $SYSPATH/$ONLINEATTR | PrintError
 			fi
+
 			#
 			# Workaround for bad drivers which report success but
 			# silently fail or have an asynchronous online processing.
--- a/zconf/chccwdev.8
+++ b/zconf/chccwdev.8
@@ -11,7 +11,7 @@ chccwdev \- modify generic attributes of
 .B chccwdev
 .RB "[ (" -a
 .IB <name> = <value>
-.RB "| " -e " | " -d " | " -f  ") [...]]"
+.RB "| " -e " | " -d " | " -s " | " -f  ") [...]]"
 .br
 .I  <range>
 .RI "[, " "<range>" " [...]]"
@@ -28,7 +28,7 @@ special.
 If the same attribute is given more than one time the value that was set
 last will be used. This is also true (while not that obvious) when mixing
 the generic
-.BR -a " and the " -e ", " -d " and " -f " arguments."
+.BR -a " and the " -e ", " -d ", "-s" and " -f " arguments."
 .P
 All attributes will be set in the following order:
 .RS
@@ -38,7 +38,8 @@ All attributes except online. If the dev
 error if the attribute doesn't exist.
 .TP
 2.
-Set the online attribute to the desired value ((forced) online or offline).
+Set the online attribute to the desired value ((forced) online or
+(safe) offline).
 .TP
 3.
 Set all the attributes that havn't been set, yet. At this point invalid
@@ -65,7 +66,7 @@ the attribute it will be read to check w
 
 .TP
 .BR -e | --online
-Try to set the given range of devices online.
+Try to set the specified devices offline.
 
 .TP
 .BR -f | --forceonline
@@ -74,8 +75,15 @@ be used to bring it online regardless of
 
 .TP
 .BR -d | --offline
-Try to set the given range of devices offline. The online and offline
-option are exclusive.
+Try to set the specified devices offline. The --online, --forceonline,
+--offline, and --safeoffline options are mutually exclusive.
+
+.TP
+.BR -s |--safeoffline
+DASD only: For each specified device, wait until all outstanding I/O
+requests have completed, and then try to set the device offline. The
+--online, --forceonline, --offline, and --safeoffline options are
+mutually exclusive.
 
 .TP
 \fB<range>\fR = <bus ID>\fB[-\fR<bus ID>\fB]\fR
@@ -89,8 +97,9 @@ Example: "0192" becomes "0.0.0192".
 .SH EXAMPLES
 \fBchccwdev --online 0.0.0192,0.0.0195-0.0.0198\fR
 .RS
-Will try to set the devices with bus ID 0.0.0192, 0.0.0195, 0.0.0196,
-0.0.0197 and 0.0.0198 online
+After completing all outstanding I/O requests for the devices with bus
+ID 0.0.0192, 0.0.0195, 0.0.0196, 0.0.0197 and 0.0.0198, tries to set
+the  device offline.
 .RE
 .P
 .B chccwdev --attribute readonly=1 --attribute online=1 0.0.0192
@@ -105,6 +114,13 @@ Set the cmb_enable attribute of the devi
 0.0.0198 to one. This would for example activate the usage of the channel
 measurement block facility.
 .RE
+.P
+.B chccwdev --safeoffline 0.0.0192,0.0.0195-0.0.0198
+.RS
+Will  try  to  set  the  devices with bus ID 0.0.0192, 0.0.0195,
+0.0.0196, 0.0.0197 and 0.0.0198 offline and finish all outstanding I/O
+requests before.
+.RE
 
 .SH AUTHOR
 .nf
