pagefault_disable的效果

发布时间 2023-04-27 00:10:27作者: 摩斯电码

在内核代码中经常看到下面的用法:

/**
 * copy_to_user_nofault(): safely attempt to write to a user-space location
 * @dst: address to write to
 * @src: pointer to the data that shall be written
 * @size: size of the data chunk
 *
 * Safely write to address @dst from the buffer at @src.  If a kernel fault
 * happens, handle that and return -EFAULT.
 */
long copy_to_user_nofault(void __user *dst, const void *src, size_t size)
{
	long ret = -EFAULT;

	if (access_ok(dst, size)) {
		pagefault_disable();
		ret = __copy_to_user_inatomic(dst, src, size);
		pagefault_enable();
	}

	if (ret)
		return -EFAULT;
	return 0;
}

上面的pagefault_disable的实现如下:

/*
 * These routines enable/disable the pagefault handler. If disabled, it will
 * not take any locks and go straight to the fixup table.
 *
 * User access methods will not sleep when called from a pagefault_disabled()
 * environment.
 */
static inline void pagefault_disable(void)
{
	pagefault_disabled_inc();
	/*
	 * make sure to have issued the store before a pagefault
	 * can hit.
	 */
	barrier();
}

static inline void pagefault_enable(void)
{
	/*
	 * make sure to issue those last loads/stores before enabling
	 * the pagefault handler again.
	 */
	barrier();
	pagefault_disabled_dec();
}

根据注释,如果缺页被关闭,当发生缺页后,在缺页处理程序中会直接利用fixup表来修复,
并不会去处理缺页。所以__copy_to_user_inatomic会返回实际要拷贝的数size。

下面是一个测试程序:

  • 驱动
点击查看代码
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/miscdevice.h>

static long mem_ioctl(struct file *file, unsigned int cmd,
			     unsigned long arg)
{
	char buf[64] = {0};
	int ret;

	pagefault_disable();

	ret = copy_from_user(buf, (void *)arg, sizeof(buf));
	printk("pagefault_disable: arg: 0x%lx ret: %d, buf: %s\n", arg, ret, buf);

	pagefault_enable();

	ret = copy_from_user(buf, (void *)arg, sizeof(buf));
	printk("pagefault_enable: arg: 0x%lx ret: %d, buf: %s\n", arg, ret, buf);

	return 0;
}

static struct file_operations memcpy_fops = {
	.owner = THIS_MODULE,
	.unlocked_ioctl = mem_ioctl,
	.compat_ioctl = mem_ioctl,
};

static struct miscdevice mem_miscdev = {
	.fops	= &memcpy_fops,
	.name	= "test_memcpy",
	.minor	= MISC_DYNAMIC_MINOR,
};

static int __init test_memcpy_init(void)
{
	int ret = 0;

	ret = misc_register(&mem_miscdev);
	if (ret)
		pr_err("misc register failed: %d\n", ret);

	return ret;
}

static void __exit test_memcpy_exit(void)
{
	misc_deregister(&mem_miscdev);
}

module_init(test_memcpy_init);
module_exit(test_memcpy_exit);

MODULE_LICENSE("GPL");

  • 应用
点击查看代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <unistd.h>

int main(int argc, const char *argv[])
{
	int fd, fd1;
	char *buf;
	int count = 0;

	fd = open("/dev/test_memcpy", O_RDWR);
	if (fd < 0) {
		perror("open failed");
		exit(-1);
	}

	fd1 = open("./test.bin", O_RDWR);
	if (fd1 < 0) {
		perror("open failed");
		exit(-1);
	}

restart:
	system("vmtouch -ve ./test.bin");

	buf = mmap(NULL, 0x800000, PROT_READ | PROT_WRITE, MAP_SHARED, fd1, 0);
	if (buf == MAP_FAILED)
		perror("map failed\n");

	ioctl(fd, 0x5a5a5a5a, buf);
	munmap(buf, 0x800000);

	count++;
	if (count < 2)
		goto restart;

	close(fd1);
	close(fd);
	return 0;
}

  • 测试结果
# /mnt/test
Evicting ./test.bin

           Files: 1
     Directories: 0
   Evicted Pages: 4096 (16M)
         Elapsed: 0.001014 seconds
Evicting ./test.bin

           Files: 1
     Directories: 0
   Evicted Pages: 4096 (16M)
         Elapsed: 0.001705 seconds

内核日志:

[199215.187377] pagefault_disable: arg: 0x7fd349400000 ret: 64, buf:
[199215.188557] pagefault_enable: arg: 0x7fd349400000 ret: 0, buf: ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
[199215.219537] pagefault_disable: arg: 0x7fd349400000 ret: 64, buf:
[199215.220701] pagefault_enable: arg: 0x7fd349400000 ret: 0, buf: ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ