18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci#include <linux/module.h>
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci#include "notifier-error-inject.h"
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_cistatic int debugfs_errno_set(void *data, u64 val)
78c2ecf20Sopenharmony_ci{
88c2ecf20Sopenharmony_ci	*(int *)data = clamp_t(int, val, -MAX_ERRNO, 0);
98c2ecf20Sopenharmony_ci	return 0;
108c2ecf20Sopenharmony_ci}
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_cistatic int debugfs_errno_get(void *data, u64 *val)
138c2ecf20Sopenharmony_ci{
148c2ecf20Sopenharmony_ci	*val = *(int *)data;
158c2ecf20Sopenharmony_ci	return 0;
168c2ecf20Sopenharmony_ci}
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ciDEFINE_SIMPLE_ATTRIBUTE_SIGNED(fops_errno, debugfs_errno_get, debugfs_errno_set,
198c2ecf20Sopenharmony_ci			"%lld\n");
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic struct dentry *debugfs_create_errno(const char *name, umode_t mode,
228c2ecf20Sopenharmony_ci				struct dentry *parent, int *value)
238c2ecf20Sopenharmony_ci{
248c2ecf20Sopenharmony_ci	return debugfs_create_file(name, mode, parent, value, &fops_errno);
258c2ecf20Sopenharmony_ci}
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic int notifier_err_inject_callback(struct notifier_block *nb,
288c2ecf20Sopenharmony_ci				unsigned long val, void *p)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	int err = 0;
318c2ecf20Sopenharmony_ci	struct notifier_err_inject *err_inject =
328c2ecf20Sopenharmony_ci		container_of(nb, struct notifier_err_inject, nb);
338c2ecf20Sopenharmony_ci	struct notifier_err_inject_action *action;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	for (action = err_inject->actions; action->name; action++) {
368c2ecf20Sopenharmony_ci		if (action->val == val) {
378c2ecf20Sopenharmony_ci			err = action->error;
388c2ecf20Sopenharmony_ci			break;
398c2ecf20Sopenharmony_ci		}
408c2ecf20Sopenharmony_ci	}
418c2ecf20Sopenharmony_ci	if (err)
428c2ecf20Sopenharmony_ci		pr_info("Injecting error (%d) to %s\n", err, action->name);
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	return notifier_from_errno(err);
458c2ecf20Sopenharmony_ci}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistruct dentry *notifier_err_inject_dir;
488c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(notifier_err_inject_dir);
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistruct dentry *notifier_err_inject_init(const char *name, struct dentry *parent,
518c2ecf20Sopenharmony_ci			struct notifier_err_inject *err_inject, int priority)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	struct notifier_err_inject_action *action;
548c2ecf20Sopenharmony_ci	umode_t mode = S_IFREG | S_IRUSR | S_IWUSR;
558c2ecf20Sopenharmony_ci	struct dentry *dir;
568c2ecf20Sopenharmony_ci	struct dentry *actions_dir;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	err_inject->nb.notifier_call = notifier_err_inject_callback;
598c2ecf20Sopenharmony_ci	err_inject->nb.priority = priority;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	dir = debugfs_create_dir(name, parent);
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	actions_dir = debugfs_create_dir("actions", dir);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	for (action = err_inject->actions; action->name; action++) {
668c2ecf20Sopenharmony_ci		struct dentry *action_dir;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci		action_dir = debugfs_create_dir(action->name, actions_dir);
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci		/*
718c2ecf20Sopenharmony_ci		 * Create debugfs r/w file containing action->error. If
728c2ecf20Sopenharmony_ci		 * notifier call chain is called with action->val, it will
738c2ecf20Sopenharmony_ci		 * fail with the error code
748c2ecf20Sopenharmony_ci		 */
758c2ecf20Sopenharmony_ci		debugfs_create_errno("error", mode, action_dir, &action->error);
768c2ecf20Sopenharmony_ci	}
778c2ecf20Sopenharmony_ci	return dir;
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(notifier_err_inject_init);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_cistatic int __init err_inject_init(void)
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci	notifier_err_inject_dir =
848c2ecf20Sopenharmony_ci		debugfs_create_dir("notifier-error-inject", NULL);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	if (!notifier_err_inject_dir)
878c2ecf20Sopenharmony_ci		return -ENOMEM;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	return 0;
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic void __exit err_inject_exit(void)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	debugfs_remove_recursive(notifier_err_inject_dir);
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cimodule_init(err_inject_init);
988c2ecf20Sopenharmony_cimodule_exit(err_inject_exit);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Notifier error injection module");
1018c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1028c2ecf20Sopenharmony_ciMODULE_AUTHOR("Akinobu Mita <akinobu.mita@gmail.com>");
103