#25 wiper.sh fails on intel SSD x25-m postville

open
nobody
None
8
2009-11-18
2009-11-17
blackSP
No

Running Ubuntu 9.10 64-bit with SSD as bootdrive

-------------------------------------------------------------------------------

wiper.sh: Linux SATA SSD TRIM utility, version 2.5, by Mark Lord.
rootdev=/dev/sda1 rdev=/dev/sda1
fsmode2: fsmode=read-write
/: fstype=ext4
freesize = 65995476 KB, reserved = 659954 KB
Preparing for online TRIM of free space on /dev/sda1 (ext4 mounted read-write at /).

This operation could silently destroy your data. Are you sure (y/N)? y
Creating temporary file (65335522 KB)..
Syncing disks..
Beginning TRIM operations..
get_trimlist=/sbin/hdparm --fibmap WIPER_TMPFILE.2828

/dev/sda:
trimming 130671048 sectors from 2144 ranges
FAILED: Input/output error
Removing temporary file..
Syncing disks..
Aborted.

Discussion

  • blackSP

    blackSP - 2009-11-18

    Very annoying bug that appears to affect many Postville users that now cannot use the TRIM function.
    Looking forward for some kind of short-term resolution.

     
  • blackSP

    blackSP - 2009-11-18
    • priority: 5 --> 8
     
  • David Carr

    David Carr - 2010-04-10

    I've attached a modified wiper.sh that works for me on an Intel 40G SSD.

     
  • David Carr

    David Carr - 2010-04-10

    Can't figure out the upload but here's the script:

    #!/bin/bash
    #
    # SATA SSD free-space TRIM utility, by Mark Lord

    VERSION=2.5

    # Copyright (C) 2009 Mark Lord. All rights reserved.
    #
    # Requires gawk, a really-recent hdparm, and various other programs.
    # This needs to be redone entirely in C, for 64-bit math, someday.
    #
    # This program is free software; you can redistribute it and/or
    # modify it under the terms of the GNU General Public License Version 2,
    # as published by the Free Software Foundation.
    #
    # This program is distributed in the hope that it would be useful,
    # but WITHOUT ANY WARRANTY; without even the implied warranty of
    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    # GNU General Public License for more details.
    #
    # You should have received a copy of the GNU General Public License
    # along with this program; if not, write to the Free Software Foundation,
    # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

    export LANG=C

    ## The usual terse usage information:
    ##
    function usage_error(){
    echo >&2
    echo "Linux tune-up (TRIM) utility for SATA SSDs"
    echo "Usage: $0 [--verbose] [--commit] <mount_point|block_device>" >&2
    echo " Eg: $0 /dev/sda1" >&2
    echo >&2
    exit 1
    }

    ## Parameter parsing for the main script.
    ## Yeah, we could use getopt here instead, but what fun would that be?
    ##

    echo
    echo "${0##*/}: Linux SATA SSD TRIM utility, version $VERSION, by Mark Lord."

    verbose=0
    commit=""
    argc=$#
    arg=""
    while [ $argc -gt 0 ]; do
    if [ "$1" = "--commit" ]; then
    commit=yes
    elif [ "$1" = "--verbose" ]; then
    verbose=1
    elif [ "$1" = "" ]; then
    usage_error
    else
    if [ "$arg" != "" ]; then
    echo "$1: too many arguments, aborting." >&2
    exit 1
    fi
    arg="$1"
    fi
    argc=$((argc - 1))
    shift
    done
    [ "$arg" = "" ] && usage_error

    ## Find a required program, or else give a nicer error message than we'd otherwise see:
    ##
    function find_prog(){
    prog="$1"
    if [ ! -x "$prog" ]; then
    prog="${prog##*/}"
    p=`type -f -P "$prog" 2>/dev/null`
    if [ "$p" = "" ]; then
    echo "$1: needed but not found, aborting." >&2
    exit 1
    fi
    prog="$p"
    [ $verbose -gt 0 ] && echo " --> using $prog instead of $1" >&2
    fi
    echo "$prog"
    }

    ## Ensure we have most of the necessary utilities available before trying to proceed:
    ##
    hash -r ## Refresh bash's cached PATH entries
    HDPARM=`find_prog /sbin/hdparm` || exit 1
    FIND=`find_prog /usr/bin/find` || exit 1
    STAT=`find_prog /usr/bin/stat` || exit 1
    GAWK=`find_prog /usr/bin/gawk` || exit 1
    BLKID=`find_prog /sbin/blkid` || exit 1
    GREP=`find_prog /bin/grep` || exit 1
    ID=`find_prog /usr/bin/id` || exit 1
    LS=`find_prog /bin/ls` || exit 1
    DF=`find_prog /bin/df` || exit 1
    RM=`find_prog /bin/rm` || exit 1

    ## I suppose this will confuse the three SELinux users out there:
    ##
    if [ `$ID -u` -ne 0 ]; then
    echo "Only the super-user can use this (try \"sudo $0\" instead), aborting." >&2
    exit 1
    fi

    ## We need a very modern hdparm, for its --fallocate and --trim-sector-ranges-stdin flags:
    ## Version 9.25 added automatic determination of safe max-size of TRIM commands.
    ##
    HDPVER=`$HDPARM -V | $GAWK '{gsub("[^0-9.]","",$2); if ($2 > 0) print ($2 * 100); else print 0; exit(0)}'`
    if [ $HDPVER -lt 925 ]; then
    echo "$HDPARM: version >= 9.25 is required, aborting." >&2
    exit 1
    fi

    ## Convert relative path "$1" into an absolute pathname, resolving all symlinks:
    ##
    function get_realpath(){
    iter=0
    p="$1"
    while [ -e "$p" -a $iter -lt 100 ]; do
    ## Strip trailing slashes:
    while [ "$p" != "/" -a "$p" != "${p%%/}" ]; do
    p="${p%%/}"
    done
    ## Split into directory:leaf portions:
    d="${p%/*}"
    t="${p##*/}"
    ## If the split worked, then cd into the directory portion:
    if [ "$d" != "" -a "$d" != "$p" ]; then
    cd -P "$d" || exit
    p="$t"
    fi
    ## If what we have left is a directory, then cd to it and print realpath:
    if [ -d "$p" ]; then
    cd -P "$p" || exit
    pwd -P
    exit
    ## Otherwise if it is a symlink, read the link and loop again:
    elif [ -h "$p" ]; then
    p="`$LS -ld "$p" | awk '{sub("^[^>]*-[>] *",""); print}'`"
    ## Otherwise, prefix $p with the cwd path and print it:
    elif [ -e "$p" ]; then
    [ "${p:0:1}" = "/" ] || p="`pwd -P`/$p"
    echo "$p"
    exit
    fi
    iter=$((iter + 1))
    done
    }

    function get_devpath(){
    dir="$1"
    kdev=`$STAT --format="%04D" "$dir" 2>/dev/null`
    [ "$kdev" = "" ] && exit 1
    major=$((0x${kdev:0:2}))
    minor=$((0x${kdev:2:2}))
    $FIND /dev -xdev -type b -exec $LS -ln {} \; | $GAWK -v major="$major," -v minor="$minor" \ '($5 == major && $6 == minor){r=$NF}END{print r}'
    }

    ## Convert "$arg" into an absolute pathname target, with no symlinks or embedded blanks:
    target="`get_realpath "$arg"`"
    if [ "$target" = "" ]; then
    [ "$arg" = "/dev/root" ] && target="`get_devpath /`"
    if [ "$target" = "" ]; then
    echo "$arg: unable to determine full pathname, aborting." >&2
    exit 1
    fi
    fi
    if [ "$target" != "${target##* }" ]; then
    echo "\"$target\": pathname has embedded blanks, aborting." >&2
    exit 1
    fi

    ## Take a first cut at online/offline determination, based on the target:
    ##
    if [ -d "$target" ]; then
    method=online
    elif [ -b "$target" ]; then
    method=offline
    else
    echo "$target: not a block device or mount point, aborting." >&2
    exit 1
    fi

    ## Find the active mount-point (fsdir) associated with a device ($1: fsdev).
    ## This is complicated, and probably still buggy, because a single
    ## device can show up under *multiple* mount points in /proc/mounts.
    ##
    function get_fsdir(){
    rw=""
    r=""
    while read -a m ; do
    pdev="${m[0]}"
    [ "$pdev" = "$1" ] || pdev="`get_realpath "$pdev"`"
    if [ "$pdev" = "$1" ]; then
    if [ "$rw" != "rw" ]; then
    rw="${m[3]:0:2}"
    r="${m[1]}"
    fi
    fi
    #echo "$pdev ${m[1]} ${m[2]} ${m[3]}"
    done
    echo -n "$r"
    }

    ## Find the device (fsdev) associated with a mount point ($1: fsdir).
    ## Since mounts can be stacked on top of each other, we return the
    ## one from the last occurance in the list from /proc/mounts.
    ##
    function get_fsdev(){ ## from fsdir
    get_realpath "`$GAWK -v p="$1" '{if ($2 == p) r=$1} END{print r}' < /proc/mounts`"
    }

    ## Find the r/w or r/o status (fsmode) of a filesystem mount point ($1: fsdir)
    ## We get it from the last occurance of the mount point in the list from /proc/mounts,
    ## and convert it to a longer human-readable string.
    ##
    function get_fsmode(){ ## from fsdir
    mode="`$GAWK -v p="$1" '{if ($2 == p) r=substr($4,1,2)} END{print r}' < /proc/mounts`"
    if [ "$mode" = "ro" ]; then
    echo "read-only"
    elif [ "$mode" = "rw" ]; then
    echo "read-write"
    else
    echo "$fsdir: unable to determine mount status, aborting." >&2
    exit 1
    fi
    }

    ## Use $DF to determine the device name associated with the root filesystem.
    ##
    ## This *usually* works, but on some distros it just returns "/dev/root",
    ## and "/dev/root" does not actually exist. We leave it like that for now,
    ## because that's the pattern such systems also use in /proc/mounts.
    ## Later, at time of use, we'll try harder to find the real rootdev.
    ##
    rdev="`($DF -P / | $GAWK '/^[/]/{print $1;exit}') 2>/dev/null`"
    rootdev="`get_realpath "$rootdev"`"
    [ "$rootdev" = "" ] && rootdev=$rdev
    [ $verbose -gt 0 ] && echo "rootdev=$rootdev rdev=$rdev"

    ## The user gave us a directory (mount point) to TRIM,
    ## which implies that we will be doing an online TRIM
    ## using --fallocate and --fibmap to find the free extents.
    ## Do some preliminary correctness/feasibility checks on fsdir:
    ##
    if [ "$method" = "online" ]; then
    ## Ensure fsdir exists and is accessible to us:
    fsdir="$target"
    cd "$fsdir" || exit 1

    if [ "$fsdir" = "/" ]; then
    fsdev="$rootdev"
    else
    ## Figure out what device holds the filesystem.
    fsdev="`get_fsdev $fsdir`"
    if [ "$fsdev" = "" ]; then
    echo "$fsdir: not found in /proc/mounts, aborting." >&2
    exit 1
    fi
    fi

    ## The root filesystem may show up as the phoney "/dev/root" device
    ## in /proc/mounts (ugh). So if we see that, then substitute the rootdev
    ## that $DF gave us earlier. But $DF may have the same problem (double ugh).
    ##
    [ ! -e "$fsdev" -a "$fsdev" = "/dev/root" ] && fsdev="$rootdev"

    ## Ensure that fsdev exists and is a block device:
    if [ ! -e "$fsdev" ]; then
    if [ "$fsdev" != "/dev/root" ]; then
    echo "$fsdev: not found" >&2
    exit 1
    fi
    rdev="`get_devpath /`"
    if [ "$rdev" = "" ]; then
    echo "$fsdev: not found" >&2
    exit 1
    fi
    fsdev="$rdev"
    fi
    if [ ! -b "$fsdev" ]; then
    echo "$fsdev: not a block device" >&2
    exit 1
    fi

    ## If it is mounted read-only, we must switch to doing an "offline" trim of fsdev:
    fsmode="`get_fsmode $fsdir`" || exit 1
    [ $verbose -gt 0 ] && echo "fsmode1: fsmode=$fsmode"
    [ "$fsmode" = "read-only" ] && method=offline
    fi

    ## This is not an "else" clause from the above, because "method" may have changed.
    ## For offline TRIM, we need the block device, and it cannot be mounted read-write:
    ##
    if [ "$method" = "offline" ]; then
    ## We might already have fsdev/fsdir from above; if not, we need to find them.
    if [ "$fsdev" = "" -o "$fsdir" = "" ]; then
    fsdev="$target"
    fsdir="`get_fsdir "$fsdev" < /proc/mounts`"
    ## More weirdness for /dev/root in /proc/mounts:
    if [ "$fsdir" = "" -a "$fsdev" = "$rootdev" ]; then
    fsdir="`get_fsdir /dev/root < /proc/mounts`"
    if [ "$fsdir" = "" ]; then
    rdev="`get_devpath /`"
    [ "$rdev" != "" ] && fsdir="`get_fsdir "$rdev" < /proc/mounts`"
    fi
    fi
    fi

    ## If the filesystem is truly not-mounted, then fsdir will still be empty here.
    ## It could be mounted, though. Read-only is fine, but read-write means we need
    ## to switch gears and do an "online" TRIM instead of an "offline" TRIM.
    ##
    if [ "$fsdir" != "" ]; then
    fsmode="`get_fsmode $fsdir`" || exit 1
    [ $verbose -gt 0 ] && echo "fsmode2: fsmode=$fsmode"
    if [ "$fsmode" = "read-write" ]; then
    method=online
    cd "$fsdir" || exit 1
    fi
    fi
    fi

    ## Use $LS to find the major number of a block device:
    ##
    function get_major(){
    $LS -ln "$1" | $GAWK '{print gensub(",","",1,$5)}'
    }

    ## At this point, we have finalized our selection of online vs. offline,
    ## and we definitely know the fsdev, as well as the fsdir (fsdir="" if not-mounted).
    ##
    ## Now guess at the underlying rawdev name, which could be exactly the same as fsdev.
    ## Then determine whether or not rawdev claims support for TRIM commands.
    ## Note that some devices lie about support, and later reject the TRIM commands.
    ##
    rawdev=`echo $fsdev | $GAWK '{print gensub("[0-9]*$","","g")}'`
    rawdev="`get_realpath "$rawdev"`"
    if [ ! -e "$rawdev" ]; then
    rawdev=""
    elif [ ! -b "$rawdev" ]; then
    rawdev=""
    elif [ "`get_major $fsdev`" -ne "`get_major $rawdev`" ]; then ## sanity check
    rawdev=""
    elif [ "`get_major $fsdev`" -ne "8" ]; then ## "SCSI" drives only; no LVM confusion for now
    echo "$rawdev: does not appear to be a SCSI/SATA SSD, aborting." >&2
    exit 1
    elif ! $HDPARM -I $rawdev | $GREP -i 'Data Set Management TRIM supported' &>/dev/null ; then
    if [ "$commit" = "yes" ]; then
    echo "$rawdev: DSM/TRIM command not supported, aborting." >&2
    #exit 1
    fi
    echo "$rawdev: DSM/TRIM command not supported (continuing with dry-run)." >&2
    fi
    if [ "$rawdev" = "" ]; then
    echo "$fsdev: unable to reliably determine the underlying physical device name, aborting" >&2
    exit 1
    fi

    ## We also need to know the offset of fsdev from the beginning of rawdev,
    ## because TRIM requires absolute sector numbers within rawdev:
    ##
    fsoffset=`$HDPARM -g "$fsdev" | $GAWK 'END {print $NF}'`

    ## Next step is to determine what type of filesystem we are dealing with (fstype):
    ##
    if [ "$fsdir" = "" ]; then
    ## Not mounted: use $BLKID to determine the fstype of fsdev:
    fstype=`$BLKID -w /dev/null -c /dev/null $fsdev 2>/dev/null | \ $GAWK '/ TYPE=".*"/{sub("^.* TYPE=\"",""); sub("[\" ][\" ]*.*$",""); print}'`
    [ $verbose -gt 0 ] && echo "$fsdev: fstype=$fstype"
    else
    ## Mounted: we could just use $BLKID here, too, but it's safer to use /proc/mounts directly:
    fstype="`$GAWK -v p="$fsdir" '{if ($2 == p) r=$3} END{print r}' < /proc/mounts`"
    [ $verbose -gt 0 ] && echo "$fsdir: fstype=$fstype"
    fi
    if [ "$fstype" = "" ]; then
    echo "$fsdev: unable to determine filesystem type, aborting." >&2
    exit 1
    fi

    ## Some helper funcs and vars for use with the xfs filesystem tools:
    ##
    function xfs_abort(){
    echo "$fsdev: unable to determine xfs filesystem ${1-parameters}, aborting." >&2
    exit 1
    }
    function xfs_trimlist(){
    $XFS_DB -r -c "freesp -d" "$fsdev" ## couldn't get this to work inline
    }
    xfs_agoffsets=""
    xfs_blksects=0

    ## We used to allow single-drive btrfs here, but it stopped working in linux-2.6.31,
    ## and Chris Mason says "unsafe at any speed" really. So it's been dropped now.
    ##
    if [ "$fstype" = "btrfs" ]; then ## hdparm --fibmap fails, due to fake 0:xx device nodes
    echo "$target: btrfs filesystem type not supported (cannot determine physical devices), aborting." >&2
    exit 1
    fi

    ## Now figure out whether we can actually do TRIM on this type of filesystem:
    ##
    if [ "$method" = "online" ]; then
    ## Print sensible error messages for some common situations,
    ## rather than failing with more confusing messages later on..
    ##
    if [ "$fstype" = "ext2" -o "$fstype" = "ext3" ]; then ## No --fallocate support
    echo "$target: cannot TRIM $fstype filesystem when mounted read-write, aborting." >&2
    exit 1
    fi

    ## Figure out if we have enough free space to even attempt TRIM:
    ##
    freesize=`$DF -P -B 1024 . | $GAWK '{r=$4}END{print r}'`
    if [ "$freesize" = "" ]; then
    echo "$fsdev: unknown to '$DF'"
    exit 1
    fi
    if [ $freesize -lt 15000 ]; then
    echo "$target: filesystem too full for TRIM, aborting." >&2
    exit 1
    fi

    ## Figure out how much space to --fallocate (later), keeping in mind
    ## that this is a live filesystem, and we need to leave some space for
    ## other concurrent activities, as well as for filesystem overhead (metadata).
    ## So, reserve at least 1% or 7500 KB, whichever is larger:
    ##
    reserved=$((freesize / 100))
    [ $reserved -lt 7500 ] && reserved=7500
    [ $verbose -gt 0 ] && echo "freesize = ${freesize} KB, reserved = ${reserved} KB"
    tmpsize=$((freesize - reserved))
    tmpfile="WIPER_TMPFILE.$$"
    get_trimlist="$HDPARM --fibmap $tmpfile"
    else
    ## We can only do offline TRIM on filesystems that we "know" about here.
    ## Currently, this includes the ext2/3/4 family, xfs, and reiserfs.
    ## The first step for any of these is to ensure that the filesystem is "clean",
    ## and immediately abort if it is not.
    ##
    get_trimlist=""
    if [ "$fstype" = "ext2" -o "$fstype" = "ext3" -o "$fstype" = "ext4" ]; then
    DUMPE2FS=`find_prog /sbin/dumpe2fs` || exit 1
    fstate="`$DUMPE2FS $fsdev 2>/dev/null | $GAWK '/^[Ff]ilesystem state:/{print $NF}' 2>/dev/null`"
    if [ "$fstate" != "clean" ]; then
    echo "$target: filesystem not clean, please run \"e2fsck $fsdev\" first, aborting." >&2
    exit 1
    fi
    get_trimlist="$DUMPE2FS $fsdev"
    elif [ "$fstype" = "xfs" ]; then
    XFS_DB=`find_prog /sbin/xfs_db` || exit 1
    XFS_REPAIR=`find_prog /sbin/xfs_repair` || exit 1
    if ! $XFS_REPAIR -n "$fsdev" &>/dev/null ; then
    echo "$fsdev: filesystem not clean, please run \"xfs_repair $fsdev\" first, aborting." >&2
    exit 1
    fi

    ## For xfs, life is more complex than with ext2/3/4 above.
    ## The $XFS_DB tool does not return absolute block numbers for freespace,
    ## but rather gives them as relative to it's allocation groups (ag's).
    ## So, we'll need to interogate it for the offset of each ag within the filesystem.
    ## The agoffsets are extracted from $XFS_DB as sector offsets within the fsdev.
    ##
    agcount=`$XFS_DB -r -c "sb" -c "print agcount" "$fsdev" | $GAWK '{print 0 + $NF}'`
    [ "$agcount" = "" -o "$agcount" = "0" ] && xfs_abort "agcount"
    xfs_agoffsets=
    i=0
    while [ $i -lt $agcount ]; do
    agoffset=`$XFS_DB -r -c "sb" -c "convert agno $i daddr" "$fsdev" \ | $GAWK '{print 0 + gensub("[( )]","","g",$2)}'`
    [ "$agoffset" = "" ] && xfs_abort "agoffset-$i"
    [ $i -gt 0 ] && [ $agoffset -le ${xfs_agoffsets##* } ] && xfs_abort "agoffset[$i]"
    xfs_agoffsets="$xfs_agoffsets $agoffset"
    i=$((i + 1))
    done
    xfs_agoffsets="${xfs_agoffsets:1}" ## strip leading space

    ## We also need xfs_blksects for later, because freespace gets listed as block numbers.
    ##
    blksize=`$XFS_DB -r -c "sb" -c "print blocksize" "$fsdev" | $GAWK '{print 0 + $NF}'`
    [ "$blksize" = "" -o "$blksize" = "0" ] && xfs_abort "block size"
    xfs_blksects=$((blksize/512))
    get_trimlist="xfs_trimlist"
    elif [ "$fstype" = "reiserfs" ]; then
    DEBUGREISERFS=`find_prog /sbin/debugreiserfs` || exit 1
    ( $DEBUGREISERFS $fsdev | grep '^Filesystem state:.consistent' ) &> /dev/null
    if [ $? -ne 0 ]; then
    echo "Please run fsck.reiserfs first, aborting." >&2
    exit 1
    fi
    get_trimlist="$DEBUGREISERFS -m $fsdev"
    fi
    if [ "$get_trimlist" = "" ]; then
    echo "$target: offline TRIM not supported for $fstype filesystems, aborting." >&2
    exit 1
    fi
    fi

    ## All ready. Now let the user know exactly what we intend to do:
    ##
    mountstatus="$fstype non-mounted"
    [ "$fsdir" = "" ] || mountstatus="$fstype mounted $fsmode at $fsdir"
    echo "Preparing for $method TRIM of free space on $fsdev ($mountstatus)."

    ## If they specified "--commit" on the command line, then prompt for confirmation first:
    ##
    if [ "$commit" = "yes" ]; then
    echo >/dev/tty
    echo -n "This operation could silently destroy your data. Are you sure (y/N)? " >/dev/tty
    read yn < /dev/tty
    if [ "$yn" != "y" -a "$yn" != "Y" ]; then
    echo "Aborting." >&2
    exit 1
    fi
    TRIM="$HDPARM --please-destroy-my-drive --trim-sector-ranges-stdin $rawdev"
    else
    echo "This will be a DRY-RUN only. Use --commit to do it for real."
    TRIM="$GAWK {}"
    fi

    ## Useful in a few places later on:
    ##
    function sync_disks(){
    echo -n "Syncing disks.. "
    sync
    echo
    }

    ## Clean up tmpfile (if any) and exit:
    ##
    function do_cleanup(){
    if [ "$method" = "online" ]; then
    if [ -e $tmpfile ]; then
    echo "Removing temporary file.."
    $RM -f $tmpfile
    fi
    sync_disks
    fi
    [ $1 -eq 0 ] && echo "Done."
    [ $1 -eq 0 ] || echo "Aborted." >&2
    exit $1
    }

    ## Prepare signal handling, in case we get interrupted while $tmpfile exists:
    ##
    function do_abort(){
    echo
    do_cleanup 1
    }
    trap do_abort SIGTERM
    trap do_abort SIGQUIT
    trap do_abort SIGINT
    trap do_abort SIGHUP

    ## For online TRIM, go ahead and create the huge temporary file.
    ## This is where we finally discover whether the filesystem actually
    ## supports --fallocate or not. Some folks will be disappointed here.
    ##
    ## Note that --fallocate does not actually write any file data to fsdev,
    ## but rather simply allocates formerly-free space to the tmpfile.
    ##
    if [ "$method" = "online" ]; then
    if [ -e "$tmpfile" ]; then
    if ! $RM -f "$tmpfile" ; then
    echo "$tmpfile: already exists and could not be removed, aborting." >&2
    exit 1
    fi
    fi
    echo -n "Creating temporary file (${tmpsize} KB).. "
    if ! $HDPARM --fallocate "${tmpsize}" $tmpfile ; then
    echo "$target: this kernel may not support 'fallocate' on a $fstype filesystem, aborting." >&2
    exit 1
    fi
    echo
    fi

    ## Finally, we are now ready to TRIM something!
    ##
    ## Feed the "get_trimlist" output into a gawk program which will
    ## extract the trimable lba-ranges (extents) and batch them together
    ## into huge --trim-sector-ranges calls.
    ##
    ## We are limited by at least one thing when doing this:
    ## 1. Some device drivers may not support more than 255 sectors
    ## full of lba:count range data per TRIM command.
    ## The latest hdparm versions now take care of that automatically.
    ##
    sync_disks
    if [ "$commit" = "yes" ]; then
    echo "Beginning TRIM operations.."
    else
    echo "Simulating TRIM operations.."
    fi
    [ $verbose -gt 0 ] && echo "get_trimlist=$get_trimlist"

    ## Begin gawk program
    GAWKPROG='
    BEGIN {
    if (xfs_agoffsets != "") {
    method = "xfs_offline"
    agcount = split(xfs_agoffsets,agoffset," ");
    }
    }
    function append_range (lba,count ,this_count){
    nsectors += count;
    while (count > 0) {
    this_count = (count > 65535) ? 65535 : count
    printf "%u:%u \n", lba, this_count
    #if (verbose) printf "%u:%u ", lba, this_count > "/dev/stderr"
    lba += this_count
    count -= this_count
    nranges++;
    }
    }
    (method == "online") { ## Output from "hdparm --fibmap", in absolute sectors:
    if (NF == 4 && $2 ~ "^[1-9][0-9]*$")
    append_range($2,$4)
    next
    }
    (method == "xfs_offline") { ## Output from xfs_db:
    if (NF == 3 && gensub("[0-9 ]","","g",$0) == "" && $1 < agcount) {
    lba = agoffset[1 + $1] + ($2 * xfs_blksects) + fsoffset
    count = $3 * xfs_blksects
    append_range(lba,count)
    }
    next
    }
    /^Block size: *[1-9]/ { ## First stage output from dumpe2fs:
    blksects = $NF / 512
    next
    }
    /^Group [1-9][0-9]*:/ { ## Second stage output from dumpe2fs:
    in_groups = 1
    next
    }
    /^ *Free blocks: [0-9]/ { ## Bulk of output from dumpe2fs:
    if (blksects && in_groups) {
    n = split(substr($0,16),f,",* *")
    for (i = 1; i <= n; ++i) {
    if (f[i] ~ "^[1-9][0-9]*-[1-9][0-9]*$") {
    split(f[i],b,"-")
    lba = (b[1] * blksects) + fsoffset
    count = (b[2] - b[1] + 1) * blksects
    append_range(lba,count)
    } else if (f[i] ~ "^[1-9][0-9]*$") {
    lba = (f[i] * blksects) + fsoffset
    count = blksects
    append_range(lba,count)
    }
    }
    next
    }
    }
    /^Reiserfs super block/ {
    method = "reiserfs"
    next
    }
    /^Blocksize: / {
    if (method == "reiserfs") {
    blksects = $2 / 512
    next
    }
    }
    /^#[0-9][0-9]*:.*Free[(]/ { ## debugreiserfs
    if (method == "reiserfs" && blksects > 0) {
    n = split($0,f)
    for (i = 4; i <= n; ++i) {
    if (f[i] ~ "^ *Free[(]") {
    if (2 == split(gensub("[^-0-9]","","g",f[i]),b,"-")) {
    lba = (b[1] * blksects) + fsoffset
    count = (b[2] - b[1] + 1) * blksects
    append_range(lba, count)
    }
    }
    }
    next
    }
    }
    END {
    if (err == 0 && commit != "yes")
    printf "(dry-run) trimming %u sectors from %u ranges\n", nsectors, nranges > "/dev/stderr"
    exit err
    }'
    ## End gawk program

    $get_trimlist 2>/dev/null | $GAWK \ -v commit="$commit" \ -v method="$method" \ -v rawdev="$rawdev" \ -v fsoffset="$fsoffset" \ -v verbose="$verbose" \ -v xfs_blksects="$xfs_blksects" \ -v xfs_agoffsets="$xfs_agoffsets" \ "$GAWKPROG" | if true; then
    while read range; do
    ((i++))
    if ((i<=512)); then
    ranges=$ranges" "$range
    else
    [ $verbose -gt 0 ] && echo -e "Trim ranges:"$ranges"\n"
    echo $ranges | $TRIM
    ranges=$range
    i=1
    fi
    done
    [ $verbose -gt 0 ] && echo -e "Trim ranges:"$ranges"\n"
    echo $ranges | $TRIM
    ranges=""
    fi

    do_cleanup $?

     
  • Nobody/Anonymous

    The script does not work on the 2.6.32 kernel.

     
  • Nobody/Anonymous

    Could you please really attach the script instead of posting it ?

     
  • Comment has been marked as spam. 
    Undo

    You can see all pending comments posted by this user  here

    Anonymous

    Anonymous - 2010-05-26

    Please add/consider that this bug also applies to the Mushkin Enhanced Callisto MKNSSDCL60GB SSD as well.

    http://ubuntuforums.org/showthread.php?t=1490602

     

Log in to post a comment.