1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3
4SYSFS=
5
6# Kselftest framework requirement - SKIP code is 4.
7ksft_skip=4
8
9prerequisite()
10{
11	msg="skip all tests:"
12
13	if [ $UID != 0 ]; then
14		echo $msg must be run as root >&2
15		exit $ksft_skip
16	fi
17
18	SYSFS=`mount -t sysfs | head -1 | awk '{ print $3 }'`
19
20	if [ ! -d "$SYSFS" ]; then
21		echo $msg sysfs is not mounted >&2
22		exit $ksft_skip
23	fi
24
25	if ! ls $SYSFS/devices/system/memory/memory* > /dev/null 2>&1; then
26		echo $msg memory hotplug is not supported >&2
27		exit $ksft_skip
28	fi
29
30	if ! grep -q 1 $SYSFS/devices/system/memory/memory*/removable; then
31		echo $msg no hot-pluggable memory >&2
32		exit $ksft_skip
33	fi
34}
35
36#
37# list all hot-pluggable memory
38#
39hotpluggable_memory()
40{
41	local state=${1:-.\*}
42
43	for memory in $SYSFS/devices/system/memory/memory*; do
44		if grep -q 1 $memory/removable &&
45		   grep -q $state $memory/state; then
46			echo ${memory##/*/memory}
47		fi
48	done
49}
50
51hotpluggable_offline_memory()
52{
53	hotpluggable_memory offline
54}
55
56hotpluggable_online_memory()
57{
58	hotpluggable_memory online
59}
60
61memory_is_online()
62{
63	grep -q online $SYSFS/devices/system/memory/memory$1/state
64}
65
66memory_is_offline()
67{
68	grep -q offline $SYSFS/devices/system/memory/memory$1/state
69}
70
71online_memory()
72{
73	echo online > $SYSFS/devices/system/memory/memory$1/state
74}
75
76offline_memory()
77{
78	echo offline > $SYSFS/devices/system/memory/memory$1/state
79}
80
81online_memory_expect_success()
82{
83	local memory=$1
84
85	if ! online_memory $memory; then
86		echo $FUNCNAME $memory: unexpected fail >&2
87		return 1
88	elif ! memory_is_online $memory; then
89		echo $FUNCNAME $memory: unexpected offline >&2
90		return 1
91	fi
92	return 0
93}
94
95online_memory_expect_fail()
96{
97	local memory=$1
98
99	if online_memory $memory 2> /dev/null; then
100		echo $FUNCNAME $memory: unexpected success >&2
101		return 1
102	elif ! memory_is_offline $memory; then
103		echo $FUNCNAME $memory: unexpected online >&2
104		return 1
105	fi
106	return 0
107}
108
109offline_memory_expect_success()
110{
111	local memory=$1
112
113	if ! offline_memory $memory; then
114		echo $FUNCNAME $memory: unexpected fail >&2
115		return 1
116	elif ! memory_is_offline $memory; then
117		echo $FUNCNAME $memory: unexpected offline >&2
118		return 1
119	fi
120	return 0
121}
122
123offline_memory_expect_fail()
124{
125	local memory=$1
126
127	if offline_memory $memory 2> /dev/null; then
128		echo $FUNCNAME $memory: unexpected success >&2
129		return 1
130	elif ! memory_is_online $memory; then
131		echo $FUNCNAME $memory: unexpected offline >&2
132		return 1
133	fi
134	return 0
135}
136
137online_all_offline_memory()
138{
139	for memory in `hotpluggable_offline_memory`; do
140		if ! online_memory_expect_success $memory; then
141			echo "$FUNCNAME $memory: unexpected fail" >&2
142			retval=1
143		fi
144	done
145}
146
147error=-12
148priority=0
149# Run with default of ratio=2 for Kselftest run
150ratio=2
151retval=0
152
153while getopts e:hp:r: opt; do
154	case $opt in
155	e)
156		error=$OPTARG
157		;;
158	h)
159		echo "Usage $0 [ -e errno ] [ -p notifier-priority ] [ -r percent-of-memory-to-offline ]"
160		exit
161		;;
162	p)
163		priority=$OPTARG
164		;;
165	r)
166		ratio=$OPTARG
167		if [ "$ratio" -gt 100 ] || [ "$ratio" -lt 0 ]; then
168			echo "The percentage should be an integer within 0~100 range"
169			exit 1
170		fi
171		;;
172	esac
173done
174
175if ! [ "$error" -ge -4095 -a "$error" -lt 0 ]; then
176	echo "error code must be -4095 <= errno < 0" >&2
177	exit 1
178fi
179
180prerequisite
181
182echo "Test scope: $ratio% hotplug memory"
183
184#
185# Online all hot-pluggable memory
186#
187hotpluggable_num=`hotpluggable_offline_memory | wc -l`
188echo -e "\t online all hot-pluggable memory in offline state:"
189if [ "$hotpluggable_num" -gt 0 ]; then
190	for memory in `hotpluggable_offline_memory`; do
191		echo "offline->online memory$memory"
192		if ! online_memory_expect_success $memory; then
193			retval=1
194		fi
195	done
196else
197	echo -e "\t\t SKIPPED - no hot-pluggable memory in offline state"
198fi
199
200#
201# Offline $ratio percent of hot-pluggable memory
202#
203hotpluggable_num=`hotpluggable_online_memory | wc -l`
204target=`echo "a=$hotpluggable_num*$ratio; if ( a%100 ) a/100+1 else a/100" | bc`
205echo -e "\t offline $ratio% hot-pluggable memory in online state"
206echo -e "\t trying to offline $target out of $hotpluggable_num memory block(s):"
207for memory in `hotpluggable_online_memory`; do
208	if [ "$target" -gt 0 ]; then
209		echo "online->offline memory$memory"
210		if offline_memory_expect_success $memory &>/dev/null; then
211			target=$(($target - 1))
212			echo "-> Success"
213		else
214			echo "-> Failure"
215		fi
216	fi
217done
218if [ "$target" -gt 0 ]; then
219	retval=1
220	echo -e "\t\t FAILED - unable to offline some memory blocks, device busy?"
221fi
222
223#
224# Online all hot-pluggable memory again
225#
226hotpluggable_num=`hotpluggable_offline_memory | wc -l`
227echo -e "\t online all hot-pluggable memory in offline state:"
228if [ "$hotpluggable_num" -gt 0 ]; then
229	for memory in `hotpluggable_offline_memory`; do
230		echo "offline->online memory$memory"
231		if ! online_memory_expect_success $memory; then
232			retval=1
233		fi
234	done
235else
236	echo -e "\t\t SKIPPED - no hot-pluggable memory in offline state"
237fi
238
239#
240# Test with memory notifier error injection
241#
242
243DEBUGFS=`mount -t debugfs | head -1 | awk '{ print $3 }'`
244NOTIFIER_ERR_INJECT_DIR=$DEBUGFS/notifier-error-inject/memory
245
246prerequisite_extra()
247{
248	msg="skip extra tests:"
249
250	/sbin/modprobe -q -r memory-notifier-error-inject
251	/sbin/modprobe -q memory-notifier-error-inject priority=$priority
252
253	if [ ! -d "$DEBUGFS" ]; then
254		echo $msg debugfs is not mounted >&2
255		exit $retval
256	fi
257
258	if [ ! -d $NOTIFIER_ERR_INJECT_DIR ]; then
259		echo $msg memory-notifier-error-inject module is not available >&2
260		exit $retval
261	fi
262}
263
264echo -e "\t Test with memory notifier error injection"
265prerequisite_extra
266
267#
268# Offline $ratio percent of hot-pluggable memory
269#
270echo 0 > $NOTIFIER_ERR_INJECT_DIR/actions/MEM_GOING_OFFLINE/error
271for memory in `hotpluggable_online_memory`; do
272	if [ $((RANDOM % 100)) -lt $ratio ]; then
273		offline_memory_expect_success $memory &>/dev/null
274	fi
275done
276
277#
278# Test memory hot-add error handling (offline => online)
279#
280echo $error > $NOTIFIER_ERR_INJECT_DIR/actions/MEM_GOING_ONLINE/error
281for memory in `hotpluggable_offline_memory`; do
282	if ! online_memory_expect_fail $memory; then
283		retval=1
284	fi
285done
286
287#
288# Online all hot-pluggable memory
289#
290echo 0 > $NOTIFIER_ERR_INJECT_DIR/actions/MEM_GOING_ONLINE/error
291online_all_offline_memory
292
293#
294# Test memory hot-remove error handling (online => offline)
295#
296echo $error > $NOTIFIER_ERR_INJECT_DIR/actions/MEM_GOING_OFFLINE/error
297for memory in `hotpluggable_online_memory`; do
298	if [ $((RANDOM % 100)) -lt $ratio ]; then
299		if ! offline_memory_expect_fail $memory; then
300			retval=1
301		fi
302	fi
303done
304
305echo 0 > $NOTIFIER_ERR_INJECT_DIR/actions/MEM_GOING_OFFLINE/error
306/sbin/modprobe -q -r memory-notifier-error-inject
307
308#
309# Restore memory before exit
310#
311online_all_offline_memory
312
313exit $retval
314