1 2 Previous Next 16 Replies Latest reply on Jul 9, 2010 2:34 PM by runeks

    Trim function works well on Linux/Ubuntu 9.10 64-bit

    blackSP

      For those waiting for an updated TRIM enabled firmware for their x25-m 80gb g2 postville:

      I flashed my drive and am trimming away!

       

      If you track the conversation from here onwards you should be all set: http://www.ocztechnologyforum.com/forum/showthread.php?t=60882&page=10

      1) download hdparm 9.27

      2) make, make install

      3) apply the latest patch mentioned in the thread above as presented on page 10 by danekl

      4) copy wiper.sh to usr/local/bin

      5) run from terminal: sudo wiper.sh --commit / or sudo wiper.sh --commit /dev/sdaN (where sdaN is your ssd)

       

      BTW, this is not real TRIM but gives the same effect. I run this each time at system start up.

       

      Skip Windows, go Ubuntu!

        • 1. Re: Trim function works well on Linux/Ubuntu 9.10 64-bit
          dbm

          That's good news. 

           

          The big companies like Intel and Microsoft need to come out of their cave mentality. They thrive own their intellectual engineering thinking that no one else but them can come up with engineering solutions that will benefit the masses.  For Intel this is readily evident, in their lack of providing even a minimal of of help to their users and the tight-lip on how their SSDs work in general.  Anandtech took their drives apart and they did a excellent job of explaining how the Intel drive works  without Intel's help.  Shame on Intel.

           

          It's going to take Microsoft and the vendors probably a year to get TRIM working harmoniously without data loss. The  Linux and the Unix brethren will have things working in months.  The proof is in your post.  As I said before in another thread Microsoft did not design Win7 to optimize the use of SSDs.  Their tactic was purely "hype" and as usual people fell for it and are now surely disappointed with its implementation.

          • 2. Re: Trim function works well on Linux/Ubuntu 9.10 64-bit
            blackSP

            FYI. here's the putput I get:

             

            [Sat Nov 21 21:06:56 CET 2009] trim starting

             

            wiper.sh: Linux SATA SSD TRIM utility, version 2.5, by Mark Lord.
            Preparing for online TRIM of free space on /dev/sda1 (ext4 mounted read-write at /).
            Creating temporary file (65301988 KB)..
            Syncing disks..
            Beginning TRIM operations..

             

            /dev/sda:
            trimming 30195709 sectors from 512 ranges
            succeeded

             

            /dev/sda:
            trimming 31358977 sectors from 512 ranges
            succeeded

             

            /dev/sda:
            trimming 31293442 sectors from 512 ranges
            succeeded

             

            /dev/sda:
            trimming 31457280 sectors from 512 ranges
            succeeded

             

            /dev/sda:
            trimming 6298568 sectors from 104 ranges
            succeeded
            Removing temporary file..
            Syncing disks..
            Done.

            [Sat Nov 21 21:07:16 CET 2009] trim finished

            • 3. Re: Trim function works well on Linux/Ubuntu 9.10 64-bit
              xout

              really nice to hear. i'm new to ssds and therefore pretty unsure how to handle my x25-m g2. i opened two threads, maybe someone could give me an advice please:

               

              - http://communities.intel.com/thread/8419

              - http://sourceforge.net/projects/hdparm/forums/forum/461704/topic/3466536

              • 4. Re: Trim function works well on Linux/Ubuntu 9.10 64-bit
                dbm

                blackSP,

                  I tried it and i'm having no success. Downloaded from sourceforge.net the hdparm 9.27 source which included wiper.sh.  I'm using 9.10 32 bit PAE mode on a Macbook pro 5,2. 6 GB memory.  Gives me an error message cannot open file.  I put some debug statements in the program to see how big the WIPEFILE is and it is huge.  It fills the remainder of the drive space.  What i'm doing wrong?  I didn't apply the patch also?. 

                 

                Appreciate any help.

                • 5. Re: Trim function works well on Linux/Ubuntu 9.10 64-bit
                  dbm

                  This is exactly the output:

                  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 = 132285104 KB, reserved = 1322851 KB
                  Preparing for online TRIM of free space on /dev/sda1 (ext4 mounted read-write at /).
                  This will be a DRY-RUN only.  Use --commit to do it for real.
                  Creating temporary file (130962253 KB)..
                  Syncing disks..
                  Simulating TRIM operations..
                  get_trimlist=/sbin/hdparm --fibmap WIPER_TMPFILE.3818
                  (dry-run) trimming 261924512 sectors from 4280 ranges
                  Removing temporary file..
                  Syncing disks..
                  Done.

                  • 6. Re: Trim function works well on Linux/Ubuntu 9.10 64-bit
                    blackSP

                    Sorry, being a complete noob I can't help you, I've been fiddeling for days myself to get wiper.sh to work!

                    I ended up registering on the OCZ form where the guys where extremely helpfull and made a few patches that were essential to get wiper to work.

                     

                    But I first followed up another suggestion: use wiper 1.9 and that worked straight away!

                     

                    But... before you do anything ensure your drive supports trim, flash it (with the firmware Intel tookoffline). Linux is no Windows 7 so your machine shouldn't hang. Mine didn't!

                    • 7. Re: Trim function works well on Linux/Ubuntu 9.10 64-bit
                      dbm

                      Thanks, blackSP.  My ssd has the right firmware. I upgraded before Intel pulled it.  I will try the wiper 1.9 version.

                      • 8. Re: Trim function works well on Linux/Ubuntu 9.10 64-bit
                        echoblack

                        I upgraded my Intel G2 80GB SSD to the newest firmware ending in HD

                        I have hdparm 9.27 installed on my Arch Linux system

                        I patched the wiper.sh v2.5 to work for the Intel SSD and named it wiper-intel.sh

                         

                        Now wiper-intel.sh works. I no longer get the "Input/Output Error"

                         

                         

                        I uploaded the patched wiper.sh to rapidshare here...

                         

                        http://rapidshare.com/files/349948975/wiper-intel.sh

                         

                        MD5sum= 90e596a399ff6660c8542bf6be1d58ce

                        SHA1sum= f1af639d00a671532069ccf6407a91a35bf94521

                         

                        Here is the output of the last run of wiper-intel.sh on the ext4 /root partition on my Intel SSD

                         

                        [XXXXX@LinuxBox ~]$ sudo ./wiper-intel.sh --commit /dev/sda2

                         

                        wiper-intel.sh: Linux SATA SSD TRIM utility, version 2.5, by Mark Lord.
                        Preparing for online TRIM of free space on /dev/sda2 (ext4 mounted read-write at /).

                         

                        This operation could silently destroy your data.  Are you sure (y/N)? y
                        Creating temporary file (45499969 KB)..
                        Syncing disks..
                        Beginning TRIM operations..

                         

                        /dev/sda:
                        trimming 31178749 sectors from 512 ranges
                        succeeded

                         

                        /dev/sda:
                        trimming 31375361 sectors from 512 ranges
                        succeeded

                         

                        /dev/sda:
                        trimming 28445834 sectors from 464 ranges
                        succeeded
                        Removing temporary file..
                        Syncing disks..
                        Done.

                        • 9. Re: Trim function works well on Linux/Ubuntu 9.10 64-bit
                          ericc

                          Hello,

                           

                          I have an Intel G2 160 GB SSD. I have not been able to download the modified script from rapidshare, but from somewhere else, even though I cannot be sure of the integrity of the script then. It first seems to be OK (maximum number of displayed ranges is 512), but I'm still having the input/output errors.

                          1) can someone confirm that it also work on 160 GB model?

                          2) if so, could you indicate an alternate trustworthy download location?

                           

                          Thanks!

                          • 10. Re: Trim function works well on Linux/Ubuntu 9.10 64-bit
                            echoblack

                            Hum, that link still works.

                             

                            Anyway, so it is not working ? LIke it quits with an Input/Output error? Or are you getting errors in /var/log/messages ?

                             

                             

                            Also, the script is farly small. You can just read it to make sure it is not running netcat on some random port and emilling off your IP to some randome email account or something like that. Hell, you may be best off to to that anyway. my point being that you can read the script there really is not need for the hash values as far as security is consered.

                             

                            ----------

                            Ok I just posted the script below. Make sure word-wraping dosen't mess up the code.

                            • 11. Re: Trim function works well on Linux/Ubuntu 9.10 64-bit
                              echoblack

                              #!/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 $?

                              • 12. Re: Trim function works well on Linux/Ubuntu 9.10 64-bit
                                ericc

                                Thanks for your answer! I have the same problem though:

                                /dev/sda:
                                trimming 154928 sectors from 164 ranges
                                FAILED: Input/output error

                                 

                                And hdparm -i shows Model=INTEL SSDSA2M160G2GC, FwRev=2CV102HD, so I have the right FW, right?

                                • 13. Re: Trim function works well on Linux/Ubuntu 9.10 64-bit
                                  ericc

                                  News from the front:

                                  I've just booted using the live CD (still 9.10 64 bit), installe hdparm 9.27 and use your modified script: it works!

                                  I will try to find out what make it fails on my install and let you know if interesting.

                                  Thanks!

                                  • 14. Re: Trim function works well on Linux/Ubuntu 9.10 64-bit
                                    blackSP

                                    As far as I know it only works with hdparm 9.27, since I installed that my disk is trimmed with every boot, no i/o errors.

                                    1 2 Previous Next