Whamcloud - gitweb
LU-17744 ldiskfs: mballoc stats fixes
[fs/lustre-release.git] / lustre / scripts / lustre_rmmod
index 6fb3366..9dca753 100755 (executable)
-#!/bin/sh
+#!/bin/bash
 #
-# remove all lustre modules.  Won't succeed if they're in use, or if you
-# manually did a 'lctl network up'.
+# Takes a list of modules and unloads them and all dependent modules.
+# If a module cannot be unloaded (e.g. it's in use), an error is
+# returned.
 ###############################################################################
 
-FSTYPE=${1:-ldiskfs}
+SCRIPT_NAME="$(basename "$0")"
+LCTL=${LCTL:-lctl}
+DEBUG='false'
+[[ -n $DEBUG_RMMOD ]] && DEBUG='true'
 
-TMP=${TMP:-/tmp}
-LUSTRE=${LUSTRE:-$(cd $(dirname $0)/..; echo $PWD)}
-LCTL=${LCTL:-"$LUSTRE/utils/lctl"}
-[ ! -f "$LCTL" ] && export LCTL=$(which lctl 2> /dev/null)
+# Print help message
+print_usage() {
+       echo "$SCRIPT_NAME -h|--help"
+       echo "$SCRIPT_NAME [-d|--debug-kernel] [modulesname...]"
+       echo
+       echo -e "\t-h, --help\t\tDisplay this help message"
+       echo -e "\t-d, --debug-kernel\tDisplay lustre kernel debug messages"
+       echo -e "\tmodulesname\t\tList of lustre modules to unload. By default"
+       echo -e "\t\t\t\tldiskfs and libcfs (and their dependencies) are"
+       echo -e "\t\t\t\tselected."
+}
 
-RMMOD=rmmod
-if [ `uname -r | cut -c 3` -eq 4 ]; then
-    RMMOD="modprobe -r"
-fi
+# Print kernel debug message for lustre modules
+print_debug() {
+       local debug_file
+       $LCTL mark "$SCRIPT_NAME : Stop debug"
+       if [[ $DEBUG_RMMOD == "-" ]]; then
+               debug_file="" # dump to stdout
+       else
+               debug_file=$DEBUG_RMMOD
+       fi
+       $LCTL debug_kernel $debug_file
+       DEBUG='false'
+}
+
+# Unload all modules dependent on $1 (exclude removal of $1)
+unload_dep_modules_exclusive() {
+       local MODULE=$1
+
+       local DEPS="$(lsmod | awk '($1 == "'$MODULE'") { print $4 }')"
+       for SUBMOD in $(echo $DEPS | tr ',' ' '); do
+               unload_dep_modules_inclusive $SUBMOD || return 1
+       done
+       return 0
+}
 
-unload_dep_module() {
-    # libcfs                107852  17 lustre,obdfilter,ost,...
-    local MODULE=$1
-    local DEPS="$(lsmod | awk '($1 == "'$MODULE'") { print $4 }' | tr ',' ' ')"
-    for SUBMOD in $DEPS; do
-        unload_dep_module $SUBMOD
-    done
-    [ "$MODULE" = "libcfs" ] && $LCTL dk $TMP/debug >/dev/null || true
-    $RMMOD $MODULE 2>/dev/null || true
-    return 0
+# Unload all modules dependent on $1 (include removal of $1)
+unload_dep_modules_inclusive() {
+       local MODULE=$1
+
+       # if $MODULE not loaded, return 0
+       lsmod | egrep -q "^\<$MODULE\>" || return 0
+       unload_dep_modules_exclusive $MODULE || return 1
+
+       if $DEBUG; then
+               if [ "$MODULE" = 'libcfs' ]; then
+                       print_debug
+               fi
+               $LCTL mark "$SCRIPT_NAME : Unload $MODULE"
+       fi
+
+       rmmod $MODULE || return 1
+       return 0
 }
 
-lsmod | grep obdclass > /dev/null && $LCTL dl
-lsmod | grep $FSTYPE > /dev/null && unload_dep_module $FSTYPE
-lsmod | grep ptlrpc > /dev/null && unload_dep_module ptlrpc
-lsmod | grep libcfs > /dev/null && unload_dep_module libcfs
+declare -a modules
+while [ $# -gt 0 ]; do
+       case "$1" in
+               -h|--help)
+                       print_usage >&2
+                       exit 0
+                       ;;
+               -d|--debug-kernel)
+                       if lsmod | egrep -q '^libcfs'; then
+                               DEBUG='true'
+                       else
+                               echo "Debug unavailable: libcfs is not loaded" >&2
+                       fi
+                       ;;
+               -*)
+                       echo "Error invalid option" >&2
+                       print_usage >&2
+                       exit 2
+                       ;;
+               *)
+                       modules+=("$1")
+                       ;;
+       esac
+       shift
+done
 
-MODULES=$($LCTL modules | awk '{ print $2 }')
-if [ -n "$MODULES" ]; then
-    echo "Modules still loaded: "
-    echo $MODULES
-    exit 1
+# To maintain backwards compatibility, ldiskfs and libcfs must be
+# unloaded if no parameters are given, or if only the ldiskfs parameter
+# is given. It's ugly, but is needed to emulate the prior functionality
+if [ "${#modules[@]}" -eq 0 ] || [ "${modules[*]}" = "ldiskfs" ]; then
+       unload_all=true
+       modules=('lnet_selftest' 'ldiskfs' 'libcfs')
+else
+       unload_all=false
+fi
+
+if [ -f /sys/kernel/debug/kmemleak ] ; then
+       cat /proc/modules >/tmp/kmemleak-modules-list.txt
+       echo scan > /sys/kernel/debug/kmemleak
+       cat /sys/kernel/debug/kmemleak > /tmp/kmemleak-before-unload.txt
+       test -s /tmp/kmemleak-before-unload.txt && logger -t leak-pre -f /tmp/kmemleak-before-unload.txt
+       rm /tmp/kmemleak-before-unload.txt
+       # Clear everything here so that only new leaks show up
+       # after module unload
+       echo clear > /sys/kernel/debug/kmemleak
 fi
-exit 0
 
+# Manage debug
+if $DEBUG; then
+       echo "Lustre debug parameters:" >&2
+       $LCTL get_param debug >&2
+       $LCTL get_param debug_mb >&2
+
+       $LCTL mark "$SCRIPT_NAME : Start debug"
+fi
+
+if $unload_all; then
+       unload_dep_modules_inclusive 'ptlrpc' || exit 1
+       # LNet may have an internal ref which can prevent LND modules from
+       # unloading. Try to drop it before unloading modules.
+       # NB: we squelch stderr because lnetctl/lctl may complain about
+       # LNet being "busy", but this is normal. We're making a best effort
+       # here.
+       # Prefer lnetctl if it is present
+       if [ -n "$(which lnetctl 2>/dev/null)" ]; then
+               lnetctl lnet unconfigure 2>/dev/null
+       elif [ -n "$(which lctl 2>/dev/null)" ]; then
+               lctl net down 2>/dev/null | grep -v "LNET ready to unload"
+       fi
+fi
+
+for mod in ${modules[*]}; do
+       unload_dep_modules_inclusive $mod || exit 1
+done
+
+if $DEBUG; then
+       print_debug
+fi
+
+if [ -f /sys/kernel/debug/kmemleak ] ; then
+       echo scan > /sys/kernel/debug/kmemleak
+       cat /sys/kernel/debug/kmemleak > /tmp/kmemleak-after-unload.txt
+       test -s /tmp/kmemleak-after-unload.txt && logger -t leak-mods -f /tmp/kmemleak-modules-list.txt && logger -t leak-post -f /tmp/kmemleak-after-unload.txt
+       rm -f /tmp/kmemleak-after-unload.txt /tmp/kmemleak-modules-list.txt
+fi
+
+exit 0