1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3
4SYSFS=
5
6prerequisite()
7{
8	msg="skip all tests:"
9
10	if [ $UID != 0 ]; then
11		echo $msg must be run as root >&2
12		exit 0
13	fi
14
15	taskset -p 01 $$
16
17	SYSFS=`mount -t sysfs | head -1 | awk '{ print $3 }'`
18
19	if [ ! -d "$SYSFS" ]; then
20		echo $msg sysfs is not mounted >&2
21		exit 0
22	fi
23
24	if ! ls $SYSFS/devices/system/cpu/cpu* > /dev/null 2>&1; then
25		echo $msg cpu hotplug is not supported >&2
26		exit 0
27	fi
28
29	echo "CPU online/offline summary:"
30	online_cpus=`cat $SYSFS/devices/system/cpu/online`
31	online_max=${online_cpus##*-}
32
33	if [[ "$online_cpus" = "$online_max" ]]; then
34		echo "$msg: since there is only one cpu: $online_cpus"
35		exit 0
36	fi
37
38	echo -e "\t Cpus in online state: $online_cpus"
39
40	offline_cpus=`cat $SYSFS/devices/system/cpu/offline`
41	if [[ "a$offline_cpus" = "a" ]]; then
42		offline_cpus=0
43	else
44		offline_max=${offline_cpus##*-}
45	fi
46	echo -e "\t Cpus in offline state: $offline_cpus"
47}
48
49#
50# list all hot-pluggable CPUs
51#
52hotpluggable_cpus()
53{
54	local state=${1:-.\*}
55
56	for cpu in $SYSFS/devices/system/cpu/cpu*; do
57		if [ -f $cpu/online ] && grep -q $state $cpu/online; then
58			echo ${cpu##/*/cpu}
59		fi
60	done
61}
62
63hotplaggable_offline_cpus()
64{
65	hotpluggable_cpus 0
66}
67
68hotpluggable_online_cpus()
69{
70	hotpluggable_cpus 1
71}
72
73cpu_is_online()
74{
75	grep -q 1 $SYSFS/devices/system/cpu/cpu$1/online
76}
77
78cpu_is_offline()
79{
80	grep -q 0 $SYSFS/devices/system/cpu/cpu$1/online
81}
82
83online_cpu()
84{
85	echo 1 > $SYSFS/devices/system/cpu/cpu$1/online
86}
87
88offline_cpu()
89{
90	echo 0 > $SYSFS/devices/system/cpu/cpu$1/online
91}
92
93online_cpu_expect_success()
94{
95	local cpu=$1
96
97	if ! online_cpu $cpu; then
98		echo $FUNCNAME $cpu: unexpected fail >&2
99		exit 1
100	elif ! cpu_is_online $cpu; then
101		echo $FUNCNAME $cpu: unexpected offline >&2
102		exit 1
103	fi
104}
105
106online_cpu_expect_fail()
107{
108	local cpu=$1
109
110	if online_cpu $cpu 2> /dev/null; then
111		echo $FUNCNAME $cpu: unexpected success >&2
112		exit 1
113	elif ! cpu_is_offline $cpu; then
114		echo $FUNCNAME $cpu: unexpected online >&2
115		exit 1
116	fi
117}
118
119offline_cpu_expect_success()
120{
121	local cpu=$1
122
123	if ! offline_cpu $cpu; then
124		echo $FUNCNAME $cpu: unexpected fail >&2
125		exit 1
126	elif ! cpu_is_offline $cpu; then
127		echo $FUNCNAME $cpu: unexpected offline >&2
128		exit 1
129	fi
130}
131
132offline_cpu_expect_fail()
133{
134	local cpu=$1
135
136	if offline_cpu $cpu 2> /dev/null; then
137		echo $FUNCNAME $cpu: unexpected success >&2
138		exit 1
139	elif ! cpu_is_online $cpu; then
140		echo $FUNCNAME $cpu: unexpected offline >&2
141		exit 1
142	fi
143}
144
145error=-12
146allcpus=0
147priority=0
148online_cpus=0
149online_max=0
150offline_cpus=0
151offline_max=0
152
153while getopts e:ahp: opt; do
154	case $opt in
155	e)
156		error=$OPTARG
157		;;
158	a)
159		allcpus=1
160		;;
161	h)
162		echo "Usage $0 [ -a ] [ -e errno ] [ -p notifier-priority ]"
163		echo -e "\t default offline one cpu"
164		echo -e "\t run with -a option to offline all cpus"
165		exit
166		;;
167	p)
168		priority=$OPTARG
169		;;
170	esac
171done
172
173if ! [ "$error" -ge -4095 -a "$error" -lt 0 ]; then
174	echo "error code must be -4095 <= errno < 0" >&2
175	exit 1
176fi
177
178prerequisite
179
180#
181# Safe test (default) - offline and online one cpu
182#
183if [ $allcpus -eq 0 ]; then
184	echo "Limited scope test: one hotplug cpu"
185	echo -e "\t (leaves cpu in the original state):"
186	echo -e "\t online to offline to online: cpu $online_max"
187	offline_cpu_expect_success $online_max
188	online_cpu_expect_success $online_max
189
190	if [[ $offline_cpus -gt 0 ]]; then
191		echo -e "\t offline to online to offline: cpu $offline_max"
192		online_cpu_expect_success $offline_max
193		offline_cpu_expect_success $offline_max
194	fi
195	exit 0
196else
197	echo "Full scope test: all hotplug cpus"
198	echo -e "\t online all offline cpus"
199	echo -e "\t offline all online cpus"
200	echo -e "\t online all offline cpus"
201fi
202
203#
204# Online all hot-pluggable CPUs
205#
206for cpu in `hotplaggable_offline_cpus`; do
207	online_cpu_expect_success $cpu
208done
209
210#
211# Offline all hot-pluggable CPUs
212#
213for cpu in `hotpluggable_online_cpus`; do
214	offline_cpu_expect_success $cpu
215done
216
217#
218# Online all hot-pluggable CPUs again
219#
220for cpu in `hotplaggable_offline_cpus`; do
221	online_cpu_expect_success $cpu
222done
223
224#
225# Test with cpu notifier error injection
226#
227
228DEBUGFS=`mount -t debugfs | head -1 | awk '{ print $3 }'`
229NOTIFIER_ERR_INJECT_DIR=$DEBUGFS/notifier-error-inject/cpu
230
231prerequisite_extra()
232{
233	msg="skip extra tests:"
234
235	/sbin/modprobe -q -r cpu-notifier-error-inject
236	/sbin/modprobe -q cpu-notifier-error-inject priority=$priority
237
238	if [ ! -d "$DEBUGFS" ]; then
239		echo $msg debugfs is not mounted >&2
240		exit 0
241	fi
242
243	if [ ! -d $NOTIFIER_ERR_INJECT_DIR ]; then
244		echo $msg cpu-notifier-error-inject module is not available >&2
245		exit 0
246	fi
247}
248
249prerequisite_extra
250
251#
252# Offline all hot-pluggable CPUs
253#
254echo 0 > $NOTIFIER_ERR_INJECT_DIR/actions/CPU_DOWN_PREPARE/error
255for cpu in `hotpluggable_online_cpus`; do
256	offline_cpu_expect_success $cpu
257done
258
259#
260# Test CPU hot-add error handling (offline => online)
261#
262echo $error > $NOTIFIER_ERR_INJECT_DIR/actions/CPU_UP_PREPARE/error
263for cpu in `hotplaggable_offline_cpus`; do
264	online_cpu_expect_fail $cpu
265done
266
267#
268# Online all hot-pluggable CPUs
269#
270echo 0 > $NOTIFIER_ERR_INJECT_DIR/actions/CPU_UP_PREPARE/error
271for cpu in `hotplaggable_offline_cpus`; do
272	online_cpu_expect_success $cpu
273done
274
275#
276# Test CPU hot-remove error handling (online => offline)
277#
278echo $error > $NOTIFIER_ERR_INJECT_DIR/actions/CPU_DOWN_PREPARE/error
279for cpu in `hotpluggable_online_cpus`; do
280	offline_cpu_expect_fail $cpu
281done
282
283echo 0 > $NOTIFIER_ERR_INJECT_DIR/actions/CPU_DOWN_PREPARE/error
284/sbin/modprobe -q -r cpu-notifier-error-inject
285