summaryrefslogtreecommitdiffstats
path: root/drivers/base/test/test_async_driver_probe.c
blob: 3465800baa6c8c6c2359b3ca9473879819ba6181 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2014 Google, Inc.
 */

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/delay.h>
#include <linux/init.h>
#include <linux/hrtimer.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/time.h>
#include <linux/numa.h>
#include <linux/nodemask.h>
#include <linux/topology.h>

#define TEST_PROBE_DELAY	(5 * 1000)	/* 5 sec */
#define TEST_PROBE_THRESHOLD	(TEST_PROBE_DELAY / 2)

static atomic_t warnings, errors, timeout, async_completed;

static int test_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;

	/*
	 * Determine if we have hit the "timeout" limit for the test if we
	 * have then report it as an error, otherwise we wil sleep for the
	 * required amount of time and then report completion.
	 */
	if (atomic_read(&timeout)) {
		dev_err(dev, "async probe took too long\n");
		atomic_inc(&errors);
	} else {
		dev_dbg(&pdev->dev, "sleeping for %d msecs in probe\n",
			 TEST_PROBE_DELAY);
		msleep(TEST_PROBE_DELAY);
		dev_dbg(&pdev->dev, "done sleeping\n");
	}

	/*
	 * Report NUMA mismatch if device node is set and we are not
	 * performing an async init on that node.
	 */
	if (dev->driver->probe_type == PROBE_PREFER_ASYNCHRONOUS) {
		if (IS_ENABLED(CONFIG_NUMA) &&
		    dev_to_node(dev) != numa_node_id()) {
			dev_warn(dev, "NUMA node mismatch %d != %d\n",
				 dev_to_node(dev), numa_node_id());
			atomic_inc(&warnings);
		}

		atomic_inc(&async_completed);
	}

	return 0;
}

static struct platform_driver async_driver = {
	.driver = {
		.name = "test_async_driver",
		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
	},
	.probe = test_probe,
};

static struct platform_driver sync_driver = {
	.driver = {
		.name = "test_sync_driver",
		.probe_type = PROBE_FORCE_SYNCHRONOUS,
	},
	.probe = test_probe,
};

static struct platform_device *async_dev[NR_CPUS * 2];
static struct platform_device *sync_dev[2];

static struct platform_device *
test_platform_device_register_node(char *name, int id, int nid)
{
	struct platform_device *pdev;
	int ret;

	pdev = platform_device_alloc(name, id);
	if (!pdev)
		return ERR_PTR(-ENOMEM);

	if (nid != NUMA_NO_NODE)
		set_dev_node(&pdev->dev, nid);

	ret = platform_device_add(pdev);
	if (ret) {
		platform_device_put(pdev);
		return ERR_PTR(ret);
	}

	return pdev;

}

static int __init test_async_probe_init(void)
{
	struct platform_device **pdev = NULL;
	int async_id = 0, sync_id = 0;
	unsigned long long duration;
	ktime_t calltime;
	int err, nid, cpu;

	pr_info("registering first set of asynchronous devices...\n");

	for_each_online_cpu(cpu) {
		nid = cpu_to_node(cpu);
		pdev = &async_dev[async_id];
		*pdev =	test_platform_device_register_node("test_async_driver",
							   async_id,
							   nid);
		if (IS_ERR(*pdev)) {
			err = PTR_ERR(*pdev);
			*pdev = NULL;
			pr_err("failed to create async_dev: %d\n", err);
			goto err_unregister_async_devs;
		}

		async_id++;
	}

	pr_info("registering asynchronous driver...\n");
	calltime = ktime_get();
	err = platform_driver_register(&async_driver);
	if (err) {
		pr_err("Failed to register async_driver: %d\n", err);
		goto err_unregister_async_devs;
	}

	duration = (unsigned long long)ktime_ms_delta(ktime_get(), calltime);
	pr_info("registration took %lld msecs\n", duration);
	if (duration > TEST_PROBE_THRESHOLD) {
		pr_err("test failed: probe took too long\n");
		err = -ETIMEDOUT;
		goto err_unregister_async_driver;
	}

	pr_info("registering second set of asynchronous devices...\n");
	calltime = ktime_get();
	for_each_online_cpu(cpu) {
		nid = cpu_to_node(cpu);
		pdev = &async_dev[async_id];

		*pdev = test_platform_device_register_node("test_async_driver",
							   async_id,
							   nid);
		if (IS_ERR(*pdev)) {
			err = PTR_ERR(*pdev);
			*pdev = NULL;
			pr_err("failed to create async_dev: %d\n", err);
			goto err_unregister_async_driver;
		}

		async_id++;
	}

	duration = (unsigned long long)ktime_ms_delta(ktime_get(), calltime);
	dev_info(&(*pdev)->dev,
		 "registration took %lld msecs\n", duration);
	if (duration > TEST_PROBE_THRESHOLD) {
		dev_err(&(*pdev)->dev,
			"test failed: probe took too long\n");
		err = -ETIMEDOUT;
		goto err_unregister_async_driver;
	}


	pr_info("registering first synchronous device...\n");
	nid = cpu_to_node(cpu);
	pdev = &sync_dev[sync_id];

	*pdev = test_platform_device_register_node("test_sync_driver",
						   sync_id,
						   NUMA_NO_NODE);
	if (IS_ERR(*pdev)) {
		err = PTR_ERR(*pdev);
		*pdev = NULL;
		pr_err("failed to create sync_dev: %d\n", err);
		goto err_unregister_async_driver;
	}

	sync_id++;

	pr_info("registering synchronous driver...\n");
	calltime = ktime_get();
	err = platform_driver_register(&sync_driver);
	if (err) {
		pr_err("Failed to register async_driver: %d\n", err);
		goto err_unregister_sync_devs;
	}

	duration = (unsigned long long)ktime_ms_delta(ktime_get(), calltime);
	pr_info("registration took %lld msecs\n", duration);
	if (duration < TEST_PROBE_THRESHOLD) {
		dev_err(&(*pdev)->dev,
			"test failed: probe was too quick\n");
		err = -ETIMEDOUT;
		goto err_unregister_sync_driver;
	}

	pr_info("registering second synchronous device...\n");
	pdev = &sync_dev[sync_id];
	calltime = ktime_get();

	*pdev = test_platform_device_register_node("test_sync_driver",
						   sync_id,
						   NUMA_NO_NODE);
	if (IS_ERR(*pdev)) {
		err = PTR_ERR(*pdev);
		*pdev = NULL;
		pr_err("failed to create sync_dev: %d\n", err);
		goto err_unregister_sync_driver;
	}

	sync_id++;

	duration = (unsigned long long)ktime_ms_delta(ktime_get(), calltime);
	dev_info(&(*pdev)->dev,
		 "registration took %lld msecs\n", duration);
	if (duration < TEST_PROBE_THRESHOLD) {
		dev_err(&(*pdev)->dev,
			"test failed: probe was too quick\n");
		err = -ETIMEDOUT;
		goto err_unregister_sync_driver;
	}

	/*
	 * The async events should have completed while we were taking care
	 * of the synchronous events. We will now terminate any outstanding
	 * asynchronous probe calls remaining by forcing timeout and remove
	 * the driver before we return which should force the flush of the
	 * pending asynchronous probe calls.
	 *
	 * Otherwise if they completed without errors or warnings then
	 * report successful completion.
	 */
	if (atomic_read(&async_completed) != async_id) {
		pr_err("async events still pending, forcing timeout\n");
		atomic_inc(&timeout);
		err = -ETIMEDOUT;
	} else if (!atomic_read(&errors) && !atomic_read(&warnings)) {
		pr_info("completed successfully\n");
		return 0;
	}

err_unregister_sync_driver:
	platform_driver_unregister(&sync_driver);
err_unregister_sync_devs:
	while (sync_id--)
		platform_device_unregister(sync_dev[sync_id]);
err_unregister_async_driver:
	platform_driver_unregister(&async_driver);
err_unregister_async_devs:
	while (async_id--)
		platform_device_unregister(async_dev[async_id]);

	/*
	 * If err is already set then count that as an additional error for
	 * the test. Otherwise we will report an invalid argument error and
	 * not count that as we should have reached here as a result of
	 * errors or warnings being reported by the probe routine.
	 */
	if (err)
		atomic_inc(&errors);
	else
		err = -EINVAL;

	pr_err("Test failed with %d errors and %d warnings\n",
	       atomic_read(&errors), atomic_read(&warnings));

	return err;
}
module_init(test_async_probe_init);

static void __exit test_async_probe_exit(void)
{
	int id = 2;

	platform_driver_unregister(&async_driver);
	platform_driver_unregister(&sync_driver);

	while (id--)
		platform_device_unregister(sync_dev[id]);

	id = NR_CPUS * 2;
	while (id--)
		platform_device_unregister(async_dev[id]);
}
module_exit(test_async_probe_exit);

MODULE_DESCRIPTION("Test module for asynchronous driver probing");
MODULE_AUTHOR("Dmitry Torokhov <dtor@chromium.org>");
MODULE_LICENSE("GPL");