#!/bin/bash
#
# 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.
###############################################################################

# 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 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
	rmmod $MODULE || return 1
	return 0
}

modules="$@"

# 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 [ -z "$modules" ] || [ "$modules" = "ldiskfs" ]; then
	modules="ptlrpc lnet_selftest ldiskfs libcfs"
fi

# 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.
if lsmod | grep -q lnet; then
	# 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
	fi
fi

for mod in ${modules[*]}; do
	unload_dep_modules_inclusive $mod || exit 1
done

exit 0
