162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci// Copyright (C) 2018 Joe Lawrence <joe.lawrence@redhat.com>
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/module.h>
762306a36Sopenharmony_ci#include <linux/kernel.h>
862306a36Sopenharmony_ci#include <linux/sched.h>
962306a36Sopenharmony_ci#include <linux/workqueue.h>
1062306a36Sopenharmony_ci#include <linux/delay.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci/* load/run-time control from sysfs writer  */
1362306a36Sopenharmony_cistatic bool block_transition;
1462306a36Sopenharmony_cimodule_param(block_transition, bool, 0644);
1562306a36Sopenharmony_ciMODULE_PARM_DESC(block_transition, "block_transition (default=false)");
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistatic void busymod_work_func(struct work_struct *work);
1862306a36Sopenharmony_cistatic DECLARE_WORK(work, busymod_work_func);
1962306a36Sopenharmony_cistatic DECLARE_COMPLETION(busymod_work_started);
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic void busymod_work_func(struct work_struct *work)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	pr_info("%s enter\n", __func__);
2462306a36Sopenharmony_ci	complete(&busymod_work_started);
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	while (READ_ONCE(block_transition)) {
2762306a36Sopenharmony_ci		/*
2862306a36Sopenharmony_ci		 * Busy-wait until the sysfs writer has acknowledged a
2962306a36Sopenharmony_ci		 * blocked transition and clears the flag.
3062306a36Sopenharmony_ci		 */
3162306a36Sopenharmony_ci		msleep(20);
3262306a36Sopenharmony_ci	}
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	pr_info("%s exit\n", __func__);
3562306a36Sopenharmony_ci}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic int test_klp_callbacks_busy_init(void)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	pr_info("%s\n", __func__);
4062306a36Sopenharmony_ci	schedule_work(&work);
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	/*
4362306a36Sopenharmony_ci	 * To synchronize kernel messages, hold the init function from
4462306a36Sopenharmony_ci	 * exiting until the work function's entry message has printed.
4562306a36Sopenharmony_ci	 */
4662306a36Sopenharmony_ci	wait_for_completion(&busymod_work_started);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	if (!block_transition) {
4962306a36Sopenharmony_ci		/*
5062306a36Sopenharmony_ci		 * Serialize output: print all messages from the work
5162306a36Sopenharmony_ci		 * function before returning from init().
5262306a36Sopenharmony_ci		 */
5362306a36Sopenharmony_ci		flush_work(&work);
5462306a36Sopenharmony_ci	}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	return 0;
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic void test_klp_callbacks_busy_exit(void)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	WRITE_ONCE(block_transition, false);
6262306a36Sopenharmony_ci	flush_work(&work);
6362306a36Sopenharmony_ci	pr_info("%s\n", __func__);
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cimodule_init(test_klp_callbacks_busy_init);
6762306a36Sopenharmony_cimodule_exit(test_klp_callbacks_busy_exit);
6862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
6962306a36Sopenharmony_ciMODULE_AUTHOR("Joe Lawrence <joe.lawrence@redhat.com>");
7062306a36Sopenharmony_ciMODULE_DESCRIPTION("Livepatch test: busy target module");
71