利用WARN_ONCE调查fbmem.c的调用关系

WARN_ONCE感觉真是神奇,可以打印函数的调用关系,很是牛逼,fbmem.c是显示驱动的统合管理模块,所以想了解下它的open,read,write函数的调用关系。
首先看一下加入的地方(注意WARN_ONCE 指定 打印出用 EXPORT_SYMBOL定义的函数)。

diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c
index 76c1ad9..3e55ffc 100644
--- a/drivers/video/fbdev/core/fbmem.c
+++ b/drivers/video/fbdev/core/fbmem.c
@@ -752,7 +752,7 @@ fb_read(struct file *file, char __user *buf, size_t count, loff_
        u8 __iomem *src;
        int c, cnt = 0, err = 0;
        unsigned long total_size;
-
+       WARN_ONCE(1, "hellomxg\n");
        if (!info || ! info->screen_base)
                return -ENODEV;

@@ -817,7 +817,7 @@ fb_write(struct file *file, const char __user *buf, size_t count
        u8 __iomem *dst;
        int c, cnt = 0, err = 0;
        unsigned long total_size;
-
+       WARN_ONCE(1, "hellomxg\n");
        if (!info || !info->screen_base)
                return -ENODEV;

@@ -1397,6 +1397,8 @@ fb_mmap(struct file *file, struct vm_area_struct * vma)
        unsigned long start;
        u32 len;

+       WARN_ONCE(1, "hellomxg\n");
+
        if (!info)
                return -ENODEV;
        fb = info->fbops;
@@ -1443,7 +1445,7 @@ __releases(&info->lock)
        int fbidx = iminor(inode);
        struct fb_info *info;
        int res = 0;
-
+       WARN_ONCE(1, "hellomxg\n");
        info = get_fb_info(fbidx);

之后随便运行个显示应用程序,或者用【cat /dev/urandom > /dev/fb0】命令,之后 dmesg中可以看到。
■fb_open函数

[  169.076711] [<c03f5020>] (fb_open) from [<c0207c58>] (chrdev_open+0x14c/0x17c)
[  169.084809] [<c0207c58>] (chrdev_open) from [<c0200edc>] (do_dentry_open+0x29c/0x310)
[  169.093599] [<c0200edc>] (do_dentry_open) from [<c0212654>] (path_openat+0xe44/0x101c)
[  169.102483] [<c0212654>] (path_openat) from [<c0212868>] (do_filp_open+0x3c/0x9c)
[  169.110874] [<c0212868>] (do_filp_open) from [<c02022f8>] (do_sys_open+0x104/0x230)
[  169.119468] [<c02022f8>] (do_sys_open) from [<c0107360>] (ret_fast_syscall+0x0/0x48)

这个函数说明前,要说一下这个 devtmpfs 文件系统了,系统初始化的时候,如果配置了devtmpfs的话,会生成一个线程 devtmpfsd,
当我们调用 device_create时,其中的 devcie_add函数内部会调用devtmpfs_create_node函数,此函数会通知 devtmpfsd线程让其调用mknod函数建立设备节点,即,函数:error = dir->i_op->mknod(dir, dentry, mode, dev);
而这个 mknod要根据devtmpfs 具体挂载到了哪个文件系统而定,比如说挂载到了ext4上了,那就调用 ext4上的 mknode函数,mknode会调用init_special_inode用来初始化此设备inode的属性

void init_special_inode(struct inode *inode, umode_t mode, dev_t rdev)
{
	inode->i_mode = mode;
	if (S_ISCHR(mode)) {
		inode->i_fop = &def_chr_fops;
		inode->i_rdev = rdev;

即,定义了 /dev/xx 这个xxinode节点的 i_fop,可以进一步看到 i_fop->open就是 chrdev_open函数。
另外 将设备号也保存了下来(inode->i_rdev = rdev)

好的,接下来看调用栈,通过 path_openat找到 /dev/xx 这个inode节点后,会生成一个file = get_empty_filp(); 并调用do_dentry_open,
do_dentry_open这个函数做了几个牛逼的事情。
struct file *f,
①f->f_inode = inode;
②f->f_op = fops_get(inode->i_fop); // f->f_open=inode->i_fop,即f->f_open=chrdev_open
③open = f->f_op->open; //即,调用函数chrdev_open
此函数呢,就会在 cdev_map 通过 mknode注册的inode->i_rdev来找 register_chrdev注册的cdev
并无耻的将 f->f_op = cdev->ops//确定了关系
struct file 里面的f_op 变成了 cdev实现的 file_operations 结构体。

■fb_write函数

[  265.724692] [<c03f44d0>] (fb_write) from [<c0202ad0>] (__vfs_write+0x28/0x12c)
[  265.732794] [<c0202ad0>] (__vfs_write) from [<c0203848>] (vfs_write+0xb8/0x190)
[  265.740983] [<c0203848>] (vfs_write) from [<c020479c>] (SyS_write+0x4c/0x9c)
[  265.748886] [<c020479c>] (SyS_write) from [<c0107360>] (ret_fast_syscall+0x0/0x48)

接下来,应用程序就通过 open生成的 struct file 来搞事情。

ssize_t __vfs_write(struct file *file, const char __user *p, size_t count,
		    loff_t *pos)
{
	if (file->f_op->write)
		return file->f_op->write(file, p, count, pos); //调用fb_write
	else if (file->f_op->write_iter)
		return new_sync_write(file, p, count, pos);
	else
		return -EINVAL;
}

■fb_mmap函数

[  169.233673] [<c03f3cb0>] (fb_mmap) from [<c01e0124>] (mmap_region+0x364/0x538)
[  169.241779] [<c01e0124>] (mmap_region) from [<c01e062c>] (do_mmap+0x334/0x3c0)
[  169.249872] [<c01e062c>] (do_mmap) from [<c01cbcf0>] (vm_mmap_pgoff+0x94/0xdc)
[  169.257982] [<c01cbcf0>] (vm_mmap_pgoff) from [<c01de788>] (SyS_mmap_pgoff+0xac/0xc4)
[  169.266768] [<c01de788>] (SyS_mmap_pgoff) from [<c0107360>] (ret_fast_syscall+0x0/0x48)

mmap_region函数内部:

error = file->f_op->mmap(file, vma);

这里顺便提一嘴 fb_mmap这个函数,此函数会调用

int vm_iomap_memory(struct vm_area_struct *vma, phys_addr_t start, unsigned long len)
{
	unsigned long pfn = start >> PAGE_SHIFT;
	unsigned long vm_len = vma->vm_end - vma->vm_start;

	pfn += vma->vm_pgoff;
	return io_remap_pfn_range(vma, vma->vm_start, pfn, vm_len, vma->vm_page_prot);
}

来对 start这个物理地址进行映射,映射到用户空间的vma里。
但是这里有个 pfn += vma->vm_pgoff,即为什么要加上vma->vm_pgoff吗?直接用 fpn不香吗?
这里我们要结合应用程序中的mmap函数进行理解,考虑下面一个场景,当应用程序用mmap映射一个设备时,mmap的最后一个参数一般我们设置为0,没去管它,它代表的是 映射的偏移量(必须是page的整数倍),比如说
假设/dev/fb0 提供的物理地址范围是: A~B
在用户层执行mmap:

map_base = mmap(NULL, 0xff, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 1*PAGESZIE); 

对应的结果是将物理地址 (A+1 * PAGESZIE)~ B 的范围 映射到了 用户空间,
即,用户空间map_base虚拟地址对应的物理地址 不是A,而是(A + 1 * PAGESZIE)即:A地址的基础上偏移了 1 * PAGESZIE,
另外:在mmap系统调用的过程中,已将vma->vm_pgoff赋值为:1 * PAGESZIE / PAGESZIE = 1;
所以不难看出,vma->vm_pgoff作为匿名页时(不是文件数据)时的作用是 :要从映射设备的物理起始地址A开始,偏移vma->vm_pgoff * PAGESZIE ,即从(A+vma->vm_pgoff * PAGESZIE)这个地址开始映射,所以vma->vm_pgoff 可以定义为:要映射的设备地址的偏移(偏移是以页为单位)。
一般我们没有偏移映射的需求,所以用户层中mmap的最后一个参数一般都是0,即vma->vm_pgoff为0。
这么这个物理地址A是指的是什么呢?
比如说LCD驱动中,驱动 probe后,就会赋值info->fix.mmio_start(显存物理地址,dma_malloc申请的连续内存,并将mmio_start内存地址赋值给lcd控制器的frambufer寄存器中)。
这个显存地址mmio_start呢,就是上边提到的地址A,需要通过用户层的mmap来映射

....
start = info->fix.smem_start;
....
return vm_iomap_memory(vma, start, len);

■fb_mmap函数再谈

fb_mmap刚开始为了获得 fb_info有一段

static struct fb_info *file_fb_info(struct file *file)
{
	struct inode *inode = file->f_path.dentry->d_inode;
	int fbidx = iminor(inode);
	struct fb_info *info = registered_fb[fbidx];

	if (info != file->private_data)
		info = NULL;
	return info;
}
fb_mmap(
struct file *file, struct vm_area_struct * vma)
{
	struct fb_info *info = file_fb_info(file);
	....
}

即获取 inode 通过 file->f_path.dentry->d_inode; 就可以获取,那个过程又是如何来的呢?
我们知道,实际上在linux下一切都是文件,/dev/xx 等设备文件当然也不例外,在 创建 设备节点的时候,当然也要申请一个 设备相关的inode,并且融入与 vfs的调用过程中,我们可以看一下,当调用节点的时候,调用mknode函数,我们参考一个简单的 mknod

static void __d_instantiate(struct dentry *dentry, struct inode *inode)
{
	spin_lock(&dentry->d_lock);
	if (inode) {
		if (unlikely(IS_AUTOMOUNT(inode)))
			dentry->d_flags |= DCACHE_NEED_AUTOMOUNT;
		list_add(&dentry->d_u.d_alias, &inode->i_dentry);
	}
	dentry->d_inode = inode;
	dentry_rcuwalk_barrier(dentry);
	spin_unlock(&dentry->d_lock);
	fsnotify_d_instantiate(dentry, inode);
}
void d_inst
antiate(struct dentry *entry, struct inode * inode)
{
	BUG_ON(!list_empty(&entry->d_u.d_alias));
	if (inode)
		spin_lock(&inode->i_lock);
	__d_instantiate(entry, inode);
	if (inode)
		spin_unlock(&inode->i_lock);
	security_d_instantiate(entry, inode);
}
static i
nt usbfs_mknod (struct inode *dir, struct dentry *dentry, umode_t mode,
			dev_t dev)
{
	struct inode *inode = usbfs_get_inode(dir->i_sb, mode, dev);
	int error = -EPERM;

	if (dentry->d_inode)
		return -EEXIST;

	if (inode) {
		d_instantiate(dentry, inode);
		dget(dentry);
		error = 0;
	}
	return error;
}

可以看到 __d_instantiate函数中建立了 【dentry->d_inode = inode】
之后当打开设备的时候,即回头在看一下 do_dentry_open 这个函数,其中里面的一段代码。

	inode = dentry->d_inode;
	if (f->f_mode & FMODE_WRITE) {
		error = __get_file_write_access(inode, mnt);
		if (error)
			goto cleanup_file;
		if (!special_file(inode->i_mode))
			file_take_write(f);
	}

	f->f_mapping = inode->i_mapping;
	f->f_path.dentry = dentry;
	f->f_path.mnt = mnt;
	f->f_pos = 0;

即 open的时候就已经 f->f_path.dentry = dentry; 了。

另外,附上根文件系统的挂载过程:
https://www.cnblogs.com/schips/p/13178870.html
简单讲,open 一个/dev/x 文件,这个 / 从哪里开始呢?
首先这个 / 是初始化文件系统时,挂载后生成的,之后将 超级快以及根的 dentry
通过 set_fs_root(current->fs, &root); 设定到 current中,之后 open函数就从 current->fs->root开始搞起
直接上这个"/“是内存建立的节点,后期通过mount挂接到 这个内存根节点”/" 下。
大致流程如下:

void __init mnt_init(void)
{
	init_rootfs();
	init_mount_tree();
}

init_rootfs函数,注册名为 rootfs的文件系统。

static struct file_system_type rootfs_fs_type = {
        .name           = "rootfs",
        .mount          = rootfs_mount,
        .kill_sb        = kill_litter_super,
};

int 
__init init_rootfs(void)
{
        int err;

        err = bdi_init(&ramfs_backing_dev_info);
        if (err)
                return err;

        err = register_filesystem(&rootfs_fs_type);
        if (err)
                bdi_destroy(&ramfs_backing_dev_info);

        return err;
}

init_mount_tree函数

static void __init init_mount_tree(void)
{
	struct vfsmount *mnt;
	struct mnt_namespace *ns;
	struct path root;

	mnt = do_kern_mount("rootfs", 0, "rootfs", NULL);
	if (IS_ERR(mnt))
		panic("Can't create rootfs");

	ns = create_mnt_ns(mnt);
	if (IS_ERR(ns))
		panic("Can't allocate initial namespace");

	init_task.nsproxy->mnt_ns = ns;
	get_mnt_ns(ns);

	root.mnt = mnt;
	root.dentry = mnt->mnt_root;

	set_fs_pwd(current->fs, &root);
	set_fs_root(current->fs, &root);
}

其中的 do_kern_mount 要挂载名为rootfs的文件系统(即上面 init_rootfs做的事儿)
此函数调用了 init_rootfs中注册的 rootfs_fs_type 的 mount函数

do_kern_mount
	->vfs_kern_mount
		->struct mount *mnt;
		->struct dentry *root;
		->mnt = alloc_vfsmnt(name);
		->root = mount_fs(type, flags, name, data);//这里调用了rootfs_mount
		->mnt->mnt.mnt_root = root;
		->mnt->mnt.mnt_sb = root->d_sb;
		->mnt->mnt_mountpoint = mnt->mnt.mnt_root;
		->mnt->mnt_parent = mnt;
					->root = type->mount(type, flags, name, data);
					->sb = root->d_sb;
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
优化这段代码: IF VR(v_alarm1).0 <> ax_alarm.ax_dial THEN VR(v_alarm1).0 = ax_alarm.ax_dial IF VR(v_alarm1).1 <> ax_alarm.ax_scr1_updown THEN VR(v_alarm1).1 = ax_alarm.ax_scr1_updown IF VR(v_alarm1).2 <> ax_alarm.ax_scr1_halftone THEN VR(v_alarm1).2 = ax_alarm.ax_scr1_halftone IF VR(v_alarm1).3 <> ax_alarm.ax_scr1_scraper THEN VR(v_alarm1).3 = ax_alarm.ax_scr1_scraper IF VR(v_alarm1).4 <> ax_alarm.ax_scr2_updown THEN VR(v_alarm1).4 = ax_alarm.ax_scr2_updown IF VR(v_alarm1).5 <> ax_alarm.ax_scr2_halftone THEN VR(v_alarm1).5 = ax_alarm.ax_scr2_halftone IF VR(v_alarm1).6 <> ax_alarm.ax_scr2_scraper THEN VR(v_alarm1).6 = ax_alarm.ax_scr2_scraper IF VR(v_alarm1).7 <> ax_alarm.ax_scr3_updown THEN VR(v_alarm1).7 = ax_alarm.ax_scr3_updown IF VR(v_alarm1).8 <> ax_alarm.ax_scr3_halftone THEN VR(v_alarm1).8 = ax_alarm.ax_scr3_halftone IF VR(v_alarm1).9 <> ax_alarm.ax_scr3_scraper THEN VR(v_alarm1).9 = ax_alarm.ax_scr3_scraper IF VR(v_alarm1).10 <> ax_alarm.ax_goin_spin THEN VR(v_alarm1).10 = ax_alarm.ax_goin_spin IF VR(v_alarm1).11 <> ax_alarm.ax_output_spin THEN VR(v_alarm1).11 = ax_alarm.ax_output_spin IF VR(v_alarm1).12 <> ax_alarm.ax_tl THEN VR(v_alarm1).12 = ax_alarm.ax_tl IF VR(v_alarm1).13 <> ax_alarm.ax_work1 THEN VR(v_alarm1).13 = ax_alarm.ax_work1 IF VR(v_alarm1).14 <> ax_alarm.ax_work2 THEN VR(v_alarm1).14 = ax_alarm.ax_work2 IF VR(v_alarm1).15 <> ax_alarm.ax_work3 THEN VR(v_alarm1).15 = ax_alarm.ax_work3 IF VR(v_alarm2).0 <> ax_alarm.ax_work4 THEN VR(v_alarm2).0 = ax_alarm.ax_work4 IF VR(v_alarm2).1 <> ax_alarm.ax_work5 THEN VR(v_alarm2).1 = ax_alarm.ax_work5 IF VR(v_alarm2).2 <> ax_alarm.ax_work6 THEN VR(v_alarm2).2 = ax_alarm.ax_work6 IF VR(v_alarm2).3 <> ax_alarm.ax_work7 THEN VR(v_alarm2).3 = ax_alarm.ax_work7 IF VR(v_alarm2).4 <> ax_alarm.ax_work8 THEN VR(v_alarm2).4 = ax_alarm.ax_work8 IF VR(v_alarm2).5 <> ax_alarm.ax_work9 THEN VR(v_alarm2).5 = ax_alarm.ax_work9 IF VR(v_alarm2).6 <> ax_alarm.ax_work10 THEN VR(v_alarm2).6 = ax_alarm.ax_work10 IF VR(v_warn1).0 <> ax_warn.ax_dial THEN VR(v_warn1).0 = ax_warn.ax_dial IF VR(v_warn1).1 <> ax_warn.ax_scr1_updown THEN VR(v_warn1).1 = ax_warn.ax_scr1_updown IF VR(v_warn1).2 <> ax_warn.ax_scr1_halftone THEN VR(v_warn1).2 = ax_warn.ax_scr1_halftone IF VR(v_warn1).3 <> ax_warn.ax_scr1_scraper THEN VR(v_warn1).3 = ax_warn.ax_scr1_scraper IF VR(v_warn1).4 <> ax_warn.ax_scr2_updown THEN VR(v_warn1).4 = ax_warn.ax_scr2_updown IF VR(v_warn1).5 <> ax_warn.ax_scr2_halftone THEN VR(v_warn1).5 = ax_warn.ax_scr2_halftone IF VR(v_warn1).6 <> ax_warn.ax_scr2_scraper THEN VR(v_warn1).6 = ax_warn.ax_scr2_scraper IF VR(v_warn1).7 <> ax_warn.ax_scr3_updown THEN VR(v_warn1).7 = ax_warn.ax_scr3_updown IF VR(v_warn1).8 <> ax_warn.ax_scr3_halftone THEN VR(v_warn1).8 = ax_warn.ax_scr3_halftone IF VR(v_warn1).9 <> ax_warn.ax_scr3_scraper THEN VR(v_warn1).9 = ax_warn.ax_scr3_scraper IF VR(v_warn1).10 <> ax_warn.ax_goin_spin THEN VR(v_warn1).10 = ax_warn.ax_goin_spin IF VR(v_warn1).11 <> ax_warn.ax_output_spin THEN VR(v_warn1).11 = ax_warn.ax_output_spin IF VR(v_warn1).12 <> ax_warn.ax_tl THEN VR(v_warn1).12 = ax_warn.ax_tl IF VR(v_warn1).13 <> ax_warn.ax_work1 THEN VR(v_warn1).13 = ax_warn.ax_work1 IF VR(v_warn1).14 <> ax_warn.ax_work2 THEN VR(v_warn1).14 = ax_warn.ax_work2 IF VR(v_warn1).15 <> ax_warn.ax_work3 THEN VR(v_warn1).15 = ax_warn.ax_work3 IF VR(v_warn2).0 <> ax_warn.ax_work4 THEN VR(v_warn2).0 = ax_warn.ax_work4 IF VR(v_warn2).1 <> ax_warn.ax_work5 THEN VR(v_warn2).1 = ax_warn.ax_work5 IF VR(v_warn2).2 <> ax_warn.ax_work6 THEN VR(v_warn2).2 = ax_warn.ax_work6 IF VR(v_warn2).3 <> ax_warn.ax_work7 THEN VR(v_warn2).3 = ax_warn.ax_work7 IF VR(v_warn2).4 <> ax_warn.ax_work8 THEN VR(v_warn2).4 = ax_warn.ax_work8 IF VR(v_warn2).5 <> ax_warn.ax_work9 THEN VR(v_warn2).5 = ax_warn.ax_work9 IF VR(v_warn2).6 <> ax_warn.ax_work10 THEN VR(v_warn2).6 = ax_warn.ax_work10
03-08

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值