162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <linux/fault-inject.h>
362306a36Sopenharmony_ci#include <linux/slab.h>
462306a36Sopenharmony_ci#include <linux/mm.h>
562306a36Sopenharmony_ci#include "slab.h"
662306a36Sopenharmony_ci
762306a36Sopenharmony_cistatic struct {
862306a36Sopenharmony_ci	struct fault_attr attr;
962306a36Sopenharmony_ci	bool ignore_gfp_reclaim;
1062306a36Sopenharmony_ci	bool cache_filter;
1162306a36Sopenharmony_ci} failslab = {
1262306a36Sopenharmony_ci	.attr = FAULT_ATTR_INITIALIZER,
1362306a36Sopenharmony_ci	.ignore_gfp_reclaim = true,
1462306a36Sopenharmony_ci	.cache_filter = false,
1562306a36Sopenharmony_ci};
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cibool __should_failslab(struct kmem_cache *s, gfp_t gfpflags)
1862306a36Sopenharmony_ci{
1962306a36Sopenharmony_ci	int flags = 0;
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci	/* No fault-injection for bootstrap cache */
2262306a36Sopenharmony_ci	if (unlikely(s == kmem_cache))
2362306a36Sopenharmony_ci		return false;
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci	if (gfpflags & __GFP_NOFAIL)
2662306a36Sopenharmony_ci		return false;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	if (failslab.ignore_gfp_reclaim &&
2962306a36Sopenharmony_ci			(gfpflags & __GFP_DIRECT_RECLAIM))
3062306a36Sopenharmony_ci		return false;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	if (failslab.cache_filter && !(s->flags & SLAB_FAILSLAB))
3362306a36Sopenharmony_ci		return false;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	/*
3662306a36Sopenharmony_ci	 * In some cases, it expects to specify __GFP_NOWARN
3762306a36Sopenharmony_ci	 * to avoid printing any information(not just a warning),
3862306a36Sopenharmony_ci	 * thus avoiding deadlocks. See commit 6b9dbedbe349 for
3962306a36Sopenharmony_ci	 * details.
4062306a36Sopenharmony_ci	 */
4162306a36Sopenharmony_ci	if (gfpflags & __GFP_NOWARN)
4262306a36Sopenharmony_ci		flags |= FAULT_NOWARN;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	return should_fail_ex(&failslab.attr, s->object_size, flags);
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic int __init setup_failslab(char *str)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	return setup_fault_attr(&failslab.attr, str);
5062306a36Sopenharmony_ci}
5162306a36Sopenharmony_ci__setup("failslab=", setup_failslab);
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci#ifdef CONFIG_FAULT_INJECTION_DEBUG_FS
5462306a36Sopenharmony_cistatic int __init failslab_debugfs_init(void)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	struct dentry *dir;
5762306a36Sopenharmony_ci	umode_t mode = S_IFREG | 0600;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	dir = fault_create_debugfs_attr("failslab", NULL, &failslab.attr);
6062306a36Sopenharmony_ci	if (IS_ERR(dir))
6162306a36Sopenharmony_ci		return PTR_ERR(dir);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	debugfs_create_bool("ignore-gfp-wait", mode, dir,
6462306a36Sopenharmony_ci			    &failslab.ignore_gfp_reclaim);
6562306a36Sopenharmony_ci	debugfs_create_bool("cache-filter", mode, dir,
6662306a36Sopenharmony_ci			    &failslab.cache_filter);
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	return 0;
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cilate_initcall(failslab_debugfs_init);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci#endif /* CONFIG_FAULT_INJECTION_DEBUG_FS */
74