All of lore.kernel.org
 help / color / mirror / Atom feed
From: Tejun Heo <teheo@suse.de>
To: "Ivan N. Zlatev" <contact@i-nz.net>
Cc: linux-ide@vger.kernel.org
Subject: Re: Seagate HDD Frequent Head Unload - hdparm/dmidecode/smartctl output
Date: Sun, 03 Aug 2008 17:23:40 +0900	[thread overview]
Message-ID: <48956B0C.3030406@suse.de> (raw)
In-Reply-To: <3db1ec7f0808020601h20b2282ch51e6d93a484ac61c@mail.gmail.com>

[-- Attachment #1: Type: text/plain, Size: 479 bytes --]

Ivan N. Zlatev wrote:
> Hi again,
> 
> It appears that the storage-fixup rule [1] doesn't work. Verbose
> output below[2]. Also I saw that you use hdparm -i and sg_inq so
> output from those for the HDD too. I badly failed to understand this
> brainf*** called bash :-), so may be you can advice what's going
> wrong? Thanks in advance.

Can you please the attached updated script?  The previous version failed
match if dmi string contains leading or trailing blanks.

-- 
tejun

[-- Attachment #2: storage-fixup --]
[-- Type: text/plain, Size: 8555 bytes --]

#! /bin/bash
#
# storage-fixup			- Tejun Heo <teheo@suse.de>
#
# Script to issue fix up commands for weird disks.  This is primarily
# to adjust ATA APM setting.  Some laptop BIOSen set this value too
# aggressively causing frequent head unloads which can kill the drive
# quickly.  This script should be called during boot and resume.  It
# examines rules from /etc/stroage-fixup.conf and executes matching
# commands.
#
# In stroage-fixup.conf, empty lines and lines starting w/ # are
# ignored.  Each line starts with rule, dmi, ata or act.
#
# rule RULENAME
#	Starts a rule.  $RULENAME can't contain whitespaces.
#
# dmi KEY PATTERN
#	Checks whether DMI value for KEY matches PATTERN.  If not, the
#	rule is skipped.
#
# ata KEY PATTERN
#	Checks whether ATA value for KEY matches PATTERN.  If not, the
#	rule is skipped.  KEY can be one of model, rev and serial.
#
# act ACTION
#	Executes ACTION on matched devices.  ACTION can contain $DEV
#	which will be substituted with device file of matching device.
#
# sact ACTION
#	Silent version of "act".  Useful when the action to take is
#	printing out a warning message.
#
# PATTERN is bash extglob pattern.
#
# For example, the following (useless) rule disables APM on the first
# harddrive of my machine.
#
# rule p5w64
# dmi baseboard-product-name	P5W64 WS Pro
# dmi baseboard-manufacturer	ASUSTeK Computer INC.
# ata model                     WDC WD5000YS-01M
# ata serial                    *WMANU1217262
# act hdparm -B 255 $DEV
#
# Release under BSD license.  See LICENSE.
#

declare usage="
Usage: storage-fixup [-h] [-V] [-v] [-b] [-c config_file] [-m max_devs]

       -h      Print this help message and exit
       -V      Print version and exit
       -v      Verbose
       -d      Dry run, don't actually execute action
       -c      Use config_file instead of /etc/storage-fixup.conf
       -m      Maximum number of allowed devices (default=64, 0 for unlimited)
"

declare dmidecode=${DMIDECODE:-dmidecode}
declare hdparm=${HDPARM:-hdparm}
declare sg_inq=${HDPARM:-sg_inq}
declare sed=${SED:-sed}

declare version=0.2
declare conf_file=/etc/storage-fixup.conf
declare max_devs=64

declare newline=$'\n'
declare dry_run=0 verbose=0 lineno=0 skip=0 rule_name="" reply
declare -a storage_devs
declare -a match_cache
declare -a matches

log() {
    echo "storage-fixup: $@"
}

warn() {
   log "$@" 1>&2
}

debug() {
    if [ $verbose -ne 0 ]; then
	warn "$@"
    fi
}

trim() {
    local str="$1"

    str="${str##*([[:blank:]])}"
    str="${str%%*([[:blank:]])}"
    echo -n "$str"
}

#
# search_match_cache - search match cache
# @type: type of match
# @key: key of property to search
# @idx: index of device to search for
#
# Searches match cache and returns 0 if found, 1 if @type:@key
# properties are cached but matching entry is not found, 2 if
# @type:@key properties are not cached yet.  On success, the matched
# property is returned in $reply.
#
search_match_cache() {
    local type="$1" key=$(trim "$2") idx="$3"
    local i key_found=0 cache len match

    reply=

    for ((i=0;i<${#match_cache[@]};i++)); do
	cache=${match_cache[i]}
	len=${#cache}

	match="${cache#$type:$key:?(-)+([0-9]) }"
	if [ ${#match} -ne $len ]; then
	    key_found=1
	fi

	match="${cache#$type:$key:$idx }"
	if [ ${#match} -ne $len ]; then
	    reply="$match"
	    return 0
	fi
    done

    if [ $key_found -eq 1 ]; then
	return 1
    else
	return 2
    fi
}

#
# add_to_match_cache - add entry to match cache
# @type: type of match
# @key: key of the entry to be added
# @idx: index of device to add entry for
# @property: property of the entry to be added
#
# Add $property for $type:$key:$idx.
#
add_to_match_cache() {
    local type="$1" key=$(trim "$2") idx="$3" property=$(trim "$4")

    match_cache+=("$type:$key:$idx $property")
    debug "C $type:$key:$idx $property"
    reply="$property"
    return 0
}

#
# do_dmi - perform DMI match
# @key: DMI key to be passed as --string argument to dmidecode
# @pattern: glob pattern to match
#
# Returns 0 on match, 1 on mismatch, 2 on invalid match (triggers
# warning).
#
do_dmi() {
    local key="$1" pattern="$2"
    local ret val

    if [ -z "$key" -o -z "$pattern" ]; then
	return 1
    fi

    search_match_cache dmi "$key" 0
    ret=$?
    if [ $ret -eq 2 ]; then
	val=$($dmidecode --string "$key")
	if [ "$?" -ne 0 ]; then
	    add_to_match_cache dmi "$key" -1
	    return 2
	fi
	add_to_match_cache dmi "$key" 0 "$val"
    elif [ $ret -eq 1 ]; then
	return 2
    fi

    if [ -z "${reply##$pattern}" ]; then
	debug "Y $lineno $rule_name dmi $key=$pattern"
	return 0
    fi

    debug "N $lineno $rule_name dmi $key=$pattern"
    return 1
}

#
# do_storage - perform storage match
# @type: ata or scsi
# @key: ata key - model, rev or serial for both ata and scsi or vendor for scsi
# @pattern: glob pattern to match
#
# Returns 0 on match, 1 on mismatch, 2 on invalid match (triggers
# warning).
#
do_storage() {
    local type="$1" key="$2" pattern="$3" idx
    local -a old_matches=("${matches[@]}")

    if [ -z "$key" -o -z "$pattern" ]; then
	return 1
    fi

    matches=()
    for idx in ${old_matches[@]}; do
	if search_match_cache $type "$key" $idx; then
	    if [ $? -eq 0 -a -z "${reply##$pattern}" ]; then
		matches+=($idx)
	    fi
	fi
    done

    if [ ${#matches[@]} -eq 0 ]; then
	debug "N $lineno $rule_name $type:$key=$pattern"
	return 1
    fi

    debug "Y $lineno $rule_name $type nr_devs=${#matches[@]} $type:$key"
    return 0
}

#
# do_act - execute action
# @act: action to execute
#
# Execute @act for each device in $matches.  "$DEV" in @act is
# substituted with the /dev node of each match.  If $dry_run is set,
# the action is logged but not actually executed.
#
# Returns 0.
#
do_act() {
    local act="$1" verbose="$2"
    local id dev

    for idx in ${matches[@]}; do
	DEV=${storage_devs[idx]}
	if [ $dry_run -eq 0 ]; then
	    if [ $verbose -ne 0 ]; then
		eval log "$rule_name: executing \"$act\""
	    fi
	    eval "$act"
	else
	    eval log "$rule_name: dry-run \"$act\""
	fi
    done

    return 0
}

#
# Execution starts here
#
shopt -s extglob

while getopts "dvVc:m:h" option; do
    case $option in
	d)
	    dry_run=1;;
	v)
	    verbose=1;;
	V)
	    echo "$version"
	    exit 0;;
	c)
	    conf_file=$OPTARG;;
	m)
	    max_devs=$((OPTARG+0));;
	*)
	    echo "$usage" 2>&1
	    exit 1;;
    esac
done

# what storage devices do we have?
storage_devs=($(ls /dev/[sh]d+([a-z]) /dev/sr+([0-9]) 2> /dev/null))
debug "I ${#storage_devs[@]} storage devices"

if [ $max_devs -ne 0 -a ${#storage_devs[@]} -gt $max_devs ]; then
    warn "nr_storage_devs=${#storage_devs[@]} > limit=$max_devs, skipping"
    exit 1
fi

# populate storage info
for ((i=0;i<${#storage_devs};i++)); do
    output=$($hdparm -i ${storage_devs[i]} 2> /dev/null)
    if [ $? -eq 0 ]; then
	MODEL=
	REV=
	SERIAL=
	eval $(echo "$output" | $sed -nr 's/^\s*Model=\s*(.*\S|\s*)\s*,\s*FwRev=\s*(.*\S|\s*)\s*,\s*SerialNo=\s*(.*\S|\s*)\s*$/MODEL=\"\1\"\nREV=\"\2\"\nSERIAL=\"\3\"/p')
	add_to_match_cache ata model $i "$MODEL"
	add_to_match_cache ata rev $i "$REV"
	add_to_match_cache ata serial $i "$SERIAL"
    fi

    output=$($sg_inq ${storage_devs[i]} 2> /dev/null)
    if [ $? -eq 0 ]; then
	VENDOR=
	MODEL=
	REV=
	SERIAL=
	eval $(echo "$output" | $sed -nr 's/^\s*Vendor identification:\s*(.*\S|\s*)\s*$/VENDOR=\"\1\"/p')
	eval $(echo "$output" | $sed -nr 's/^\s*Product identification:\s*(.*\S|\s*)\s*$/MODEL=\"\1\"/p')
	eval $(echo "$output" | $sed -nr 's/^\s*Product revision level:\s*(.*\S|\s*)\s*$/REV=\"\1\"/p')
	eval $(echo "$output" | $sed -nr 's/^\s*Unit serial number:\s*(.*\S|\s*)\s*$/SERIAL=\"\1\"/p')
	add_to_match_cache scsi vendor $i "$VENDOR"
	add_to_match_cache scsi model $i "$MODEL"
	add_to_match_cache scsi rev $i "$REV"
	add_to_match_cache scsi serial $i "$SERIAL"
    fi
done

while read f0 f1 f2; do
    true $((lineno++))
    if [ -z ${f0###*} ]; then
	continue
    fi

    if [ "$f0" = rule ]; then
	rule_name=$f1
	skip=0
	matches=($(seq 0 $((${#storage_devs[@]}-1))))
	continue
    fi

    if [ $skip -ne 0 ]; then
	continue
    fi

    case "$f0" in
    dmi)
	    do_dmi "$f1" "$f2"
	    ;;
    ata)
	    do_storage ata "$f1" "$f2"
	    ;;
    scsi)
	    do_storage scsi "$f1" "$f2"
	    ;;
    act)
	    do_act "$f1 $f2" 1
	    ;;
    sact)
	    do_act "$f1 $f2" 0
	    ;;
    *)
	    false
	    ;;
    esac

    ret=$?
    if [ $ret -ne 0 ]; then
	if [ $ret -eq 2 ]; then
	    warn "malformed line $lineno \"$f0 $f1 $f2\","\
	         "skipping rule $rule_name" 2>&1
	fi
	skip=1
    fi
done < $conf_file

  reply	other threads:[~2008-08-03  8:24 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2008-07-26 13:34 Seagate HDD Frequent Head Unload - hdparm/dmidecode/smartctl output Ivan N. Zlatev
2008-07-28  7:28 ` Tejun Heo
2008-07-28  9:04   ` Ivan N. Zlatev
2008-07-28  9:18     ` Tejun Heo
2008-08-02 13:01       ` Ivan N. Zlatev
2008-08-03  8:23         ` Tejun Heo [this message]
2008-08-03 15:29           ` Ivan N. Zlatev
2008-08-03 22:42             ` Tejun Heo

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=48956B0C.3030406@suse.de \
    --to=teheo@suse.de \
    --cc=contact@i-nz.net \
    --cc=linux-ide@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.