/* * GPL HEADER START * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 only, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License version 2 for more details (a copy is included * in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU General Public License * version 2 along with this program; If not, see * http://www.gnu.org/licenses/gpl-2.0.html * * GPL HEADER END */ /* * Copyright 2017 Cray Inc. All rights reserved. * Author: Frank Zago. */ /* Check that the inode number is the same whether the call to * vfs_getattr is coming from a system call or from a kthread. When * CONFIG_X86_X32 was set, the result used to be different for * Lustre. In addition, a user can also check that the same inode * number is also seen from the kernel and userspace. */ #include #include #include #include #include #include /* Random ID passed by userspace, and printed in messages, used to * separate different runs of that module. */ static int run_id; module_param(run_id, int, 0644); MODULE_PARM_DESC(run_id, "run ID"); /* Name of the file to stat. */ static char fname[4096]; module_param_string(fname, fname, sizeof(fname), 0644); MODULE_PARM_DESC(fname, "name of file to stat"); static DECLARE_COMPLETION(thr_start); #define PREFIX "lustre_kinode_%u:" static int stat_file(struct kstat *stbuf) { struct file *fd; int rc; fd = filp_open(fname, O_RDONLY, 0); if (IS_ERR(fd)) { pr_err(PREFIX " can't open file %s\n", run_id, fname); return -EIO; } #if defined(HAVE_USER_NAMESPACE_ARG) || defined(HAVE_INODEOPS_ENHANCED_GETATTR) rc = vfs_getattr(&fd->f_path, stbuf, STATX_INO, AT_STATX_SYNC_AS_STAT); #elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) rc = vfs_getattr(&fd->f_path, stbuf); #else rc = vfs_getattr(fd->f_path.mnt, fd->f_path.dentry, stbuf); #endif if (rc != 0) { pr_err(PREFIX " vfs_getattr failed: %d\n", run_id, rc); goto out; } pr_err(PREFIX " inode is %llu\n", run_id, stbuf->ino); rc = 0; out: filp_close(fd, NULL); return rc; } static int stat_thread(void *data) { struct kstat *stbuf = data; int rc; /* Signal caller that thread has started. */ complete(&thr_start); rc = stat_file(stbuf); /* Wait for call to kthread_stop. */ set_current_state(TASK_INTERRUPTIBLE); while (!kthread_should_stop()) { schedule(); set_current_state(TASK_INTERRUPTIBLE); } set_current_state(TASK_RUNNING); return rc; } static int __init kinode_init(void) { struct task_struct *thr; struct kstat stbuf1; struct kstat stbuf2; int rc; #ifdef CONFIG_X86_X32 pr_err(PREFIX " CONFIG_X86_X32 is set\n", run_id); #else pr_err(PREFIX " CONFIG_X86_X32 is not set\n", run_id); #endif if (strlen(fname) < 1) { pr_err(PREFIX " invalid file name '%s'\n", run_id, fname); goto out; } rc = stat_file(&stbuf1); if (rc) { pr_err(PREFIX " direct stat failed: %d\n", run_id, rc); goto out; } /* Run the same from a kthread. */ thr = kthread_run(stat_thread, &stbuf2, "kinode_%u", run_id); if (IS_ERR(thr)) { pr_err(PREFIX " Cannot create kthread\n", run_id); goto out; } /* Wait for the thread to start, then wait for it to * terminate. */ wait_for_completion(&thr_start); rc = kthread_stop(thr); if (rc) { pr_err(PREFIX " indirect stat failed: %d\n", run_id, rc); goto out; } if (stbuf1.ino != stbuf2.ino) pr_err(PREFIX " inode numbers are different: %llu %llu\n", run_id, stbuf1.ino, stbuf2.ino); else /* below message is checked in sanity.sh test_129 */ pr_err(PREFIX " inode numbers are identical: %llu\n", run_id, stbuf1.ino); out: /* Don't load. */ return -EINVAL; } static void __exit kinode_exit(void) { } MODULE_AUTHOR("OpenSFS, Inc. "); MODULE_DESCRIPTION("Lustre inode stat test module"); MODULE_VERSION(LUSTRE_VERSION_STRING); MODULE_LICENSE("GPL"); module_init(kinode_init); module_exit(kinode_exit);