From 18e0f00ea2e3e5528b4ac9671d5caa3e3110fa0e Mon Sep 17 00:00:00 2001
From: Liu Zixian <liuzixian4@huawei.com>
Date: Sat, 15 May 2021 09:04:53 +0000
Subject: [PATCH] oom: add more info for oom

kernel do not printk enough messages when OS happen oom.
this module print more info when oom happen.

Signed-off-by: Liu Zixian <liuzixian4@huawei.com>
---
 src/oom_debug_info/Makefile         |   2 +
 src/oom_debug_info/oom_debug_info.c | 639 ++++++++++++++++++++++++++++
 src/oom_debug_info/oom_debug_info.h |  19 +
 3 files changed, 660 insertions(+)
 create mode 100755 src/oom_debug_info/Makefile
 create mode 100644 src/oom_debug_info/oom_debug_info.c
 create mode 100644 src/oom_debug_info/oom_debug_info.h

diff --git a/src/oom_debug_info/Makefile b/src/oom_debug_info/Makefile
new file mode 100755
index 0000000..b53d93e
--- /dev/null
+++ b/src/oom_debug_info/Makefile
@@ -0,0 +1,2 @@
+
+obj-m += oom_debug_info.o
diff --git a/src/oom_debug_info/oom_debug_info.c b/src/oom_debug_info/oom_debug_info.c
new file mode 100644
index 0000000..cba8eef
--- /dev/null
+++ b/src/oom_debug_info/oom_debug_info.c
@@ -0,0 +1,639 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. ALL rights reversed.
+ */
+
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/hugetlb.h>
+#include <linux/kallsyms.h>
+#include <linux/kernel.h>
+#include <linux/mman.h>
+#include <linux/module.h>
+#include <linux/nmi.h>
+#include <linux/oom.h>
+#include <linux/printk.h>
+#include <linux/rwsem.h>
+#include <linux/sched/signal.h>
+#include <linux/sched/task.h>
+#include <linux/seq_file.h>
+#include <linux/swap.h>
+#ifdef CONFIG_CMA
+#include <linux/cma.h>
+#endif
+#include <../fs/mount.h>
+#include <../mm/slab.h>
+
+#include "oom_debug_info.h"
+
+#define TMP_FS_ID 1
+#define FILE_PATH_BUF_SIZE 1024
+#define MAX_PRINT_STR_SIZE 1024
+#define SHOW_FILE_NUM_IN_DIR 1024
+#define RECURSE_DEPTH_LIMIT 50
+#define FILE_NUM_MDELAY_FOR_LOG 1000
+#define MAX_PRINT_FILE_INFO_BYTES (2 * 1024 * 1024UL)
+#define MAX_BUF_LEN 256
+
+int sysctl_oom_enhance_enable = 1;
+int sysctl_oom_print_file_info = 1;
+int sysctl_oom_show_file_num_in_dir = 10;
+
+static atomic_t console_silent_atomic = ATOMIC_INIT(0);
+static atomic_t dentry_tree_running = ATOMIC_INIT(0);
+static char file_path_buf[FILE_PATH_BUF_SIZE];
+static char str_print[MAX_PRINT_STR_SIZE];
+static unsigned long min_heap[SHOW_FILE_NUM_IN_DIR];
+static unsigned int fs_id;
+static char *mount_path[] = {
+	"",
+	"/tmp",
+};
+
+static unsigned long show_file_num;
+static unsigned long print_file_info_bytes;
+
+static const char *task_state_Array[] = {
+	"R (running)",		/*      0 */
+	"S (sleeping)",		/*      1 */
+	"D (disk sleep)",	/*      2 */
+	"T (stopped)",		/*      4 */
+	"T (tracing stop)",	/*      8 */
+	"Z (zombie)",		/* 16 */
+	"X (dead)"		/* 32 */
+};
+
+/* get_task_state() */
+#ifndef TASK_REPORT
+#define TASK_REPORT (TASK_RUNNING | TASK_INTERRUPTIBLE |     \
+		     TASK_UNINTERRUPTIBLE | __TASK_STOPPED | \
+		     __TASK_TRACED)
+#endif
+
+static struct ctl_table_header *debug_table_header;
+
+static struct ctl_table debug_kernel_table[] = {
+	{
+	 .procname = "oom_enhance_enable",
+	 .data = &sysctl_oom_enhance_enable,
+	 .maxlen = sizeof(int),
+	 .mode = 0640,
+	 .proc_handler = proc_dointvec_minmax,
+	 },
+	{
+	 .procname = "oom_print_file_info",
+	 .data = &sysctl_oom_print_file_info,
+	 .maxlen = sizeof(int),
+	 .mode = 0640,
+	 .proc_handler = proc_dointvec_minmax,
+	 },
+	{
+	 .procname = "oom_show_file_num_in_dir",
+	 .data = &sysctl_oom_show_file_num_in_dir,
+	 .maxlen = sizeof(int),
+	 .mode = 0640,
+	 .proc_handler = proc_dointvec_minmax,
+	 },
+	{}
+};
+
+static struct ctl_table debug_root_table[] = {
+	{
+	 .procname = "kernel",
+	 .mode = 0555,
+	 .child = debug_kernel_table,
+	 },
+	{}
+};
+
+// TODO: compat to kernel-5.10
+
+static struct rw_semaphore *k_namespace_sem = NULL;
+static rwlock_t *k_tasklist_lock = NULL;
+
+typedef void (*show_mem_fn)(unsigned int, nodemask_t *);
+show_mem_fn k_show_mem = NULL;
+
+typedef void (*iterate_supers_fn)(void (*)(struct super_block *, void *), void *);
+iterate_supers_fn k_iterate_supers = NULL;
+
+typedef void (*hugetlb_report_meminfo_fn)(struct seq_file *);
+hugetlb_report_meminfo_fn k_hugetlb_report_meminfo = NULL;
+
+static int last_console_printk = 0;
+static int *k_console_printk = NULL;
+
+static inline void _console_silent(void)
+{
+	last_console_printk = *k_console_printk;
+	*k_console_printk = CONSOLE_LOGLEVEL_SILENT;
+}
+
+static inline void _console_resume(void)
+{
+	*k_console_printk = last_console_printk;
+}
+
+static int init_kernel_sym(void)
+{
+	k_namespace_sem = (struct rw_semaphore *) kallsyms_lookup_name("namespace_sem");
+
+	k_tasklist_lock = (rwlock_t *) kallsyms_lookup_name("tasklist_lock");
+	if (!k_tasklist_lock) {
+		printk(KERN_WARNING "tasklist_lock is not found\n");
+		return -1;
+	}
+
+	k_console_printk = (int*) kallsyms_lookup_name("console_printk");
+	if (!k_console_printk) {
+		printk(KERN_WARNING "console_printk is not found\n");
+		return -1;
+	}
+
+	k_show_mem = (show_mem_fn) kallsyms_lookup_name("show_mem");
+	if (!k_show_mem) {
+		printk(KERN_WARNING "show_mem is not found\n");
+		return -1;
+	}
+
+	k_iterate_supers = (iterate_supers_fn) kallsyms_lookup_name("iterate_supers");
+	if (!k_iterate_supers) {
+		printk(KERN_WARNING "iterate_supers is not found\n");
+		return -1;
+	}
+
+	k_hugetlb_report_meminfo = (hugetlb_report_meminfo_fn) kallsyms_lookup_name("hugetlb_report_meminfo");
+	if (!k_hugetlb_report_meminfo) {
+		printk(KERN_WARNING "hugetlb_report_meminfo is not found\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static struct dentry *get_mountpoint_from_mnt_root(struct dentry *dentry)
+{
+	struct mnt_namespace *ns = current->nsproxy->mnt_ns;
+	struct mount *mnt;
+	struct dentry *mount_point = NULL;
+
+	if (IS_ERR_OR_NULL(dentry) || strncmp(dentry->d_iname, "/", sizeof("/")))
+		goto out;
+
+	if (!k_namespace_sem) {
+		printk(KERN_WARNING "k_namespace_sem is NULL\n");
+		return NULL;
+	}
+
+	if (!down_read_trylock(k_namespace_sem)) {
+		printk(KERN_WARNING "get namespace_sem fail when get mountpoint from mnt_root!\n");
+		return NULL;
+	}
+
+	list_for_each_entry(mnt, &ns->list, mnt_list) {
+		if (mnt->mnt.mnt_root == dentry) {
+			mount_point = mnt->mnt_mountpoint;
+			break;
+		}
+	}
+	up_read(k_namespace_sem);
+out:
+	return mount_point;
+}
+
+static int show_proc_slab_info(void)
+{
+// TODO: read file /proc/slabinfo
+	return 0;
+}
+
+static int show_proc_mem_info(void)
+{
+// TODO:  read file /proc/meminfo
+	return 0;
+}
+
+/*
+ * get string of task status according to stat of task_struct.
+ */
+static inline const char *oom_get_task_state(struct task_struct *tsk)
+{
+	unsigned int state = (tsk->state & TASK_REPORT) | tsk->exit_state;
+	const char **p = &task_state_Array[0];
+
+	while (state) {
+		p++;
+		state >>= 1;
+	}
+	return *p;
+}
+
+/*
+ * refer to ps -aux, show task info for debug.
+ */
+static int show_ps_aux_info(void)
+{
+	struct task_struct *p;
+
+	printk(KERN_WARNING "%5s %5s %20s %8s %8s %16s\n", "PID", "PPID", "VSZ", "RSS", "STAT", "COMMAND");
+	read_lock(k_tasklist_lock);
+	for_each_process(p) {
+		printk(KERN_WARNING "%5d %5d %7luK %7luK %16s %s\n", p->pid,
+		       p->parent->pid,
+		       (p->mm != NULL ? p->mm->total_vm : 0) * 4,
+		       (p->mm != NULL ? get_mm_rss(p->mm) : 0) * 4, oom_get_task_state(p), p->comm);
+	}
+	read_unlock(k_tasklist_lock);
+	return 0;
+}
+
+static char *get_file_path(struct dentry *dentry)
+{
+	/* for placing '\0' at end */
+	int byte_left = FILE_PATH_BUF_SIZE - 2;
+	int name_len;
+
+	memset(file_path_buf, 0, FILE_PATH_BUF_SIZE);
+
+	while (dentry && (strncmp(dentry->d_name.name, "/", 1) != 0)) {
+		name_len = strlen(dentry->d_name.name);
+		if (byte_left <= name_len) {
+			printk(KERN_WARNING "file name too long ...");
+			break;
+		}
+
+		byte_left -= name_len;
+		memcpy(file_path_buf + byte_left, dentry->d_name.name, name_len);
+		byte_left--;
+		file_path_buf[byte_left] = '/';
+		dentry = dentry->d_parent;
+	}
+
+	return file_path_buf + byte_left;
+}
+
+static bool is_empty_dir(struct dentry *dir)
+{
+	struct dentry *dentry = NULL;
+
+	if (!d_is_dir(dir))
+		return false;
+
+	if (list_empty(&dir->d_subdirs))
+		return true;
+
+	spin_lock(&dir->d_lock);
+	list_for_each_entry(dentry, &dir->d_subdirs, d_child) {
+		if (dentry == NULL || dentry->d_inode == NULL)
+			continue;
+		if (d_is_file(dentry) && dentry->d_inode->i_size > 0) {
+			spin_unlock(&dir->d_lock);
+			return false;
+		}
+		if (d_is_dir(dentry) && !is_empty_dir(dentry)) {
+			spin_unlock(&dir->d_lock);
+			return false;
+		}
+	}
+	spin_unlock(&dir->d_lock);
+	return true;
+}
+
+static void my_swap(unsigned long *a, int i, int j)
+{
+	unsigned long temp = a[i];
+
+	a[i] = a[j];
+	a[j] = temp;
+}
+
+static void heap_adjust(unsigned long *a, int n, int i)
+{
+	int j = 2 * i + 1;
+
+	while (j < n) {
+		if (j + 1 < n && a[j] > a[j + 1])
+			j++;
+		if (a[i] > a[j])
+			my_swap(a, i, j);
+		else
+			break;
+		i = j;
+		j = 2 * i + 1;
+	}
+}
+
+static void heap_build(unsigned long *a, int n)
+{
+	int i;
+
+	for (i = n / 2 - 1; i >= 0; i--)
+		heap_adjust(a, n, i);
+}
+
+static unsigned long get_topn_size_in_dir(struct dentry *dir, unsigned int *file_num)
+{
+	struct dentry *dentry = NULL;
+	unsigned long topn_size = 0;
+
+	*file_num = 0;
+	memset(min_heap, 0, sizeof(min_heap));
+
+	spin_lock(&dir->d_lock);
+	list_for_each_entry(dentry, &dir->d_subdirs, d_child) {
+		if (dentry == NULL || dentry->d_inode == NULL)
+			continue;
+		if (d_is_file(dentry)) {
+			(*file_num)++;
+			if (*file_num < sysctl_oom_show_file_num_in_dir) {
+				min_heap[*file_num - 1] = dentry->d_inode->i_size;
+			} else if (*file_num == sysctl_oom_show_file_num_in_dir) {
+				min_heap[*file_num - 1] = dentry->d_inode->i_size;
+				heap_build(min_heap, sysctl_oom_show_file_num_in_dir);
+			} else if (*file_num > sysctl_oom_show_file_num_in_dir) {
+				if (dentry->d_inode->i_size > min_heap[0]) {
+					min_heap[0] = dentry->d_inode->i_size;
+					heap_adjust(min_heap, sysctl_oom_show_file_num_in_dir, 0);
+				}
+			}
+		}
+	}
+	spin_unlock(&dir->d_lock);
+
+	if (*file_num > sysctl_oom_show_file_num_in_dir)
+		topn_size = min_heap[0];
+	return topn_size;
+}
+
+static unsigned long do_show_dentry_tree(struct dentry *tree, unsigned int depth)
+{
+	struct dentry *dentry = NULL;
+	unsigned long topn_size;
+	unsigned long dir_size = 0;
+	unsigned long subdir_size;
+	unsigned int file_num;
+	int num_chars;
+
+	if (print_file_info_bytes > MAX_PRINT_FILE_INFO_BYTES) {
+		printk(KERN_WARNING "file info exceed %lu bytes!\n", MAX_PRINT_FILE_INFO_BYTES);
+		return 0;
+	}
+
+	if (depth >= RECURSE_DEPTH_LIMIT) {
+		printk(KERN_WARNING "dir depth exceed %d!\n", RECURSE_DEPTH_LIMIT);
+		return 0;
+	}
+
+	/* get topN file size in dir */
+	topn_size = get_topn_size_in_dir(tree, &file_num);
+
+	spin_lock(&tree->d_lock);
+	/* ramfs and tmpfs will "pin the dentry in core",
+	 * so we can iterate all files in root initramfs(tmpfs) by dentry.
+	 */
+	list_for_each_entry(dentry, &tree->d_subdirs, d_child) {
+		if (dentry == NULL || dentry->d_inode == NULL)
+			continue;
+		if (dentry->d_inode->i_size == 0)
+			continue;
+		if (d_is_symlink(dentry))
+			continue;
+		if (d_is_file(dentry)) {
+			if (dentry->d_inode->i_size >= topn_size) {
+				num_chars = snprintf(str_print,
+						     MAX_PRINT_STR_SIZE, "%s:%llu\n",
+						     dentry->d_name.name, dentry->d_inode->i_size);
+				if (num_chars > 0)
+					print_file_info_bytes += num_chars;
+				printk(KERN_CONT "%s", str_print);
+				show_file_num++;
+			}
+			if (show_file_num % FILE_NUM_MDELAY_FOR_LOG == 0) {
+				num_chars = snprintf(str_print, MAX_PRINT_STR_SIZE,
+						     "already printed %lu files, will mdelay 10 ms for log system.\n",
+						     show_file_num);
+				if (num_chars > 0)
+					print_file_info_bytes += num_chars;
+				printk(KERN_CONT "%s", str_print);
+				touch_nmi_watchdog();
+				mdelay(10);
+			}
+			dir_size += dentry->d_inode->i_size;
+		}
+	}
+	/* second iterate list for showing subdirs after showing files */
+	list_for_each_entry(dentry, &tree->d_subdirs, d_child) {
+		if (dentry == NULL || dentry->d_inode == NULL)
+			continue;
+		/* 1.we use d_mountpoint to skip mountpoint, but
+		 * auditd mounted /usr/lib/modules to itself process space,
+		 * it will lead to skip this dir in memory rootfs by mistake.
+		 * 2.you can fix it by all inodes have same super block in same
+		 * file system, pity that all inodes have same super block in
+		 * root initramfs(tmpfs) when unpack, lead to it cann`t work by
+		 * dentry iterate, although you can get distinguished
+		 * inode->i_sb->s_dev by name lookup, refer to vfs_lstat to get
+		 * stat.dev, but it shouldn`t sched or alloc when oom.
+		 * 3.you also can fix it by refer to is_local_mountpoint,
+		 * notice down_read may sleep and up_read call wake_up_process
+		 * when hold d_lock, use down_read_trylock and call
+		 * local_irq_save and preempt_disable preempt_enable_no_resched
+		 * to ensure safe.
+		 * 4.we handle it in simple way, treat as mountpoint is empty
+		 * dir in initramfs.
+		 */
+		if (is_empty_dir(dentry))
+			continue;
+		if (d_is_dir(dentry)) {
+			num_chars = snprintf(str_print, MAX_PRINT_STR_SIZE,
+					     "%s%s\n", mount_path[fs_id], get_file_path(dentry));
+			if (num_chars > 0)
+				print_file_info_bytes += num_chars;
+			printk(KERN_CONT "%s", str_print);
+			subdir_size = do_show_dentry_tree(dentry, depth + 1);
+			if (subdir_size > 0) {
+				num_chars = snprintf(str_print,
+						     MAX_PRINT_STR_SIZE, "dir %s%s:%lu\n",
+						     mount_path[fs_id], get_file_path(dentry), subdir_size);
+				if (num_chars > 0)
+					print_file_info_bytes += num_chars;
+				printk(KERN_CONT "%s", str_print);
+				dir_size += subdir_size;
+			}
+		}
+	}
+	spin_unlock(&tree->d_lock);
+
+	if (topn_size > 0) {
+		num_chars = snprintf(str_print, MAX_PRINT_STR_SIZE, "...files num:%d\n", file_num);
+		if (num_chars > 0)
+			print_file_info_bytes += num_chars;
+		printk(KERN_CONT "%s", str_print);
+	}
+
+	return dir_size;
+}
+
+static void show_dentry_tree(struct dentry *tree)
+{
+	unsigned long dir_size;
+
+	if (IS_ERR_OR_NULL(tree))
+		return;
+	printk(KERN_WARNING "%s%s\n", mount_path[fs_id], tree->d_iname);
+	dir_size = do_show_dentry_tree(tree, 0);
+	printk(KERN_WARNING "%lu bytes in %s%s\n", dir_size, mount_path[fs_id], tree->d_iname);
+}
+
+static void show_file_info_by_name(struct super_block *sb, void *name)
+{
+	struct dentry *mount_point = NULL;
+
+	if (strncmp(sb->s_type->name, name, strlen(name)))
+		return;
+
+	if (!strncmp(name, "rootfs", strlen(name))) {
+		fs_id = 0;
+		show_dentry_tree(sb->s_root);
+	} else if (!strncmp(name, "tmpfs", strlen(name))) {
+		/* get mount_point of mnt_root, I have no better way than refer
+		 * to is_local_mountpoint, notice down_read may sleep and
+		 * up_read call wake_up_process, use down_read_trylock and
+		 * call preempt_disable preempt_enable_no_resched.
+		 */
+		mount_point = get_mountpoint_from_mnt_root(sb->s_root);
+		if (!strncmp(get_file_path(mount_point), "/tmp", sizeof("/tmp"))
+		    && !strncmp(mount_point->d_sb->s_type->name, "rootfs", sizeof("rootfs"))) {
+			printk(KERN_WARNING "###################\n");
+			printk(KERN_WARNING "show files in /tmp:\n");
+			fs_id = TMP_FS_ID;
+			show_dentry_tree(sb->s_root);
+		}
+	}
+}
+
+void show_rootfs_info(void)
+{
+	printk(KERN_WARNING "rootfs file info:\n");
+	k_iterate_supers(show_file_info_by_name, "rootfs");
+}
+
+void show_tmpfs_info(void)
+{
+	k_iterate_supers(show_file_info_by_name, "tmpfs");
+}
+
+void show_memfs_info(void)
+{
+	if (!sysctl_oom_print_file_info)
+		return;
+
+	if (atomic_cmpxchg(&dentry_tree_running, 0, 1))
+		return;
+	print_file_info_bytes = 0;
+	show_file_num = 0;
+	show_rootfs_info();
+	show_tmpfs_info();
+	atomic_set(&dentry_tree_running, 0);
+}
+
+void oom_show_debug_info(void)
+{
+	typedef int (*ivpfn) (void);
+	ivpfn show_pfn_memory_info = NULL;
+
+	if (atomic_cmpxchg(&console_silent_atomic, 0, 1) == 1) {
+		mdelay(500);
+		return;
+	}
+
+	dump_stack();
+
+	/* print the use info of all memory */
+	show_pfn_memory_info = (ivpfn) kallsyms_lookup_name("MEM_PrintAllMemory");
+	if (show_pfn_memory_info != NULL) {
+		printk(KERN_EMERG "\nmem trace:\n");
+		show_pfn_memory_info();
+	} else {
+		printk(KERN_EMERG "error, MEM_PrintAllMemory is NULL!\n");
+	}
+
+	/* print slab info */
+	printk(KERN_EMERG "\nslab info:\n");
+	show_proc_slab_info();
+
+	/* print mem info */
+	printk(KERN_EMERG "\nmem info:\n");
+	show_proc_mem_info();
+
+	/* silent console for console print too slow */
+	_console_silent();
+	/* print the info of current process */
+	printk(KERN_EMERG "ps -aux:\n");
+	show_ps_aux_info();
+	_console_resume();
+
+	/* print mem */
+	k_show_mem(0, NULL);
+
+	/* silent console for console print too slow */
+	_console_silent();
+	show_memfs_info();
+	_console_resume();
+
+	atomic_set(&console_silent_atomic, 0);
+}
+
+static int debug_oom_notify(struct notifier_block *self, unsigned long notused, void *nfreed)
+{
+	if (sysctl_oom_enhance_enable)
+		oom_show_debug_info();
+	return 0;
+}
+
+static struct notifier_block debug_oom_nb = {
+	.notifier_call = debug_oom_notify
+};
+
+static int __init oom_debug_info_init(void)
+{
+	int ret;
+
+	printk("oom_debug_info init\n");
+
+	ret = init_kernel_sym();
+	if (ret) {
+		return ret;
+	}
+
+	ret = register_oom_notifier(&debug_oom_nb);
+	if (ret) {
+		printk("register_oom_notifier fail\n");
+		return ret;
+	}
+
+	debug_table_header = register_sysctl_table(debug_root_table);
+	if (!debug_table_header) {
+		printk("register_sysctl_table fail\n");
+		ret = -ENOMEM;
+		goto sysctl_fail;
+	}
+
+	return 0;
+
+sysctl_fail:
+	unregister_oom_notifier(&debug_oom_nb);
+	return ret;
+}
+
+static void __exit oom_debug_info_exit(void)
+{
+	printk("oom_debug_info exit\n");
+	unregister_oom_notifier(&debug_oom_nb);
+	unregister_sysctl_table(debug_table_header);
+}
+
+module_init(oom_debug_info_init)
+module_exit(oom_debug_info_exit)
+
+MODULE_LICENSE("GPL");
diff --git a/src/oom_debug_info/oom_debug_info.h b/src/oom_debug_info/oom_debug_info.h
new file mode 100644
index 0000000..d562290
--- /dev/null
+++ b/src/oom_debug_info/oom_debug_info.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved.
+ */
+#ifndef _OOM_SHOW_INFO_H
+#define _OOM_SHOW_INFO_H
+#ifdef CONFIG_EULEROS_DEBUG_OOM
+#include <linux/nodemask.h>
+#include <linux/seq_file.h>
+
+extern int sysctl_oom_enhance_enable;
+extern int sysctl_oom_print_file_info;
+extern int sysctl_oom_show_file_num_in_dir;
+
+extern unsigned long kallsyms_lookup_name(const char *name);
+extern void oom_show_debug_info(nodemask_t *nodemask);
+extern struct dentry *get_mountpoint_from_mnt_root(struct dentry *dentry);
+#endif
+#endif
-- 
2.29.2

