linux driver probe deferral 机制

news2024/10/6 20:35:43

1. 背景介绍

在偶然的一次实验中(具体是pinctrl实验),我发现有些平台的pincontroller驱动起得很晚,而pinctrl client驱动却起得很早,在设备驱动模型中probe之前又会进行管脚复用的相关设置,按照常理来讲,这就产生了某种依赖性: pincontroller必须尽早启动,否则pinctrl client无法使用管脚复用功能,但实际上的效果并非如此,尽管pincontroller驱动起得很晚,但是client仍然能够正常使用pinctrl子系统提供的复用功能,这就是延迟probe机制。

  

2. 提交说明

我在github上找到了probe延迟机制的提交,最原始的提交如下链接,后续有人陆续在上面修改BUG:

drivercore: Add driver probe deferral mechanism · torvalds/linux@d1c3414 · GitHub

我们可以看看他的提交描述,引入这一机制解决什么样的问题: 

 为了解决驱动间的顺序依赖,引入了该机制后驱动间的顺序依赖就解耦了。

3. probe延迟机制的具体说明

该机制的主要引入是引入在drivers/base/dd.c中的,并且为struct device结构引入了一个链表节点来挂载被延迟probe的设备 :

简述一下该机制:

1).  在dev与drv匹配成功后的really_probe()中,如果驱动与设备因为某种原因无法probe成功,那么probe返回-EPROBE_DEFER表示自己需要延迟probe。

2). 这个时候调用driver_deferred_probe_add(dev)将设备加入延迟probe的链表中。

3). 处理deferred_probe_pending_list链表有两个时间点

第一个时间点是某些dev和drv probe成功后的driver_bound()中:

但是这个时机一般driver_deferred_probe_trigger()是无效的:

 

第二个时间点是late_initcall(deferred_probe_initcall):

 

在这个时机不仅真正创建了执行延迟probe的工作队列deferwq,还真正处理了deferred_probe_pending_list中挂载的节点进行延迟probe。

可以看到这个时机真的很晚了(late_initcall), 正如描述中所说: "this initcall makes sure that deferred probing is delayed until late_initcall time"。

deferred_probe_initcall()

    ->driver_deferred_probe_trigger()

        ->deferred_probe_work_func()

            ->bus_probe_device()

4). late_initcall之后

类似于ko这种场景,deferred probe就靠driver_bound()去触发,因为此时driver_deferred_probe_enable为true,且工人队列已经建立,driver_deferred_probe_trigger()就生效了。

4. 实验

为了模拟上述deferred probe机制,我构造了两个驱动: driver1.c与driver2.c,并且使他们产生依赖: driver2.c依赖driver1.c,否则probe不成功。

代码如下:driver1.c

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/platform_device.h>

extern int g_val;

static int driver1_dummy_probe(struct platform_device *dev)
{
    printk("[LJW]driver1_dummy_probe=====>\n");

    /* driver1 modified the g_val */
    g_val = 1;

    printk("[LJW]driver1_dummy_probe<=====\n");
    return 0;    
}

static int driver1_dummy_remove(struct platform_device *dev)
{
    printk("[LJW]driver1_dummy_remove=====>\n");

    printk("[LJW]driver1_dummy_remove<=====\n");
    return 0;  
}

static struct platform_device driver1_dummy_device = {
    .name = "driver1_compatible",
};

static struct platform_driver driver1_dummy_driver = {
    .probe = driver1_dummy_probe,
    .remove = driver1_dummy_remove,
    .driver = {
        .name = "driver1_compatible",
    },
};

static int __init driver1_init(void)
{
    int ret;

    printk("[LJW]driver1_init=====>\n");

    ret = platform_device_register(&driver1_dummy_device);
    if (ret < 0) {
        printk("[FAILED]platform_device_register failed for driver1_dummy_device!\n");
        return -1;
    }
    printk("[SUCCESS]platform_device_register for driver1_dummy_device\n");

    ret = platform_driver_register(&driver1_dummy_driver);
    if (ret < 0) {
        printk("[FAILED]platform_driver_register failed for driver1_dummy_driver!\n");
        platform_device_unregister(&driver1_dummy_device);
        return -1;
    }
    printk("[SUCCESS]platform_driver_register for driver1_dummy_driver\n");

    return 0;
}

static void __exit driver1_exit(void)
{
    platform_driver_unregister(&driver1_dummy_driver);
    platform_device_unregister(&driver1_dummy_device);
    return;
}

module_init(driver1_init);
module_exit(driver1_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("liaojunwu");

代码如下: driver2.c

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/platform_device.h>

int g_val = 0;
EXPORT_SYMBOL(g_val);

static int driver2_dummy_probe(struct platform_device *dev)
{
    printk("[LJW]driver2_dummy_probe=====>\n");

    /* driver1 modified the g_val */
    if (g_val == 0) {
        printk("[LJW]driver2 probe failed, return -EPROBE_DEFER!\n");
        return -EPROBE_DEFER;
    }

    printk("[LJW]driver2_dummy_probe<=====\n");
    return 0;    
}

static int driver2_dummy_remove(struct platform_device *dev)
{
    printk("[LJW]driver2_dummy_remove=====>\n");

    printk("[LJW]driver2_dummy_remove<=====\n");
    return 0;  
}

static struct platform_device driver2_dummy_device = {
    .name = "driver2_compatible",
};

static struct platform_driver driver2_dummy_driver = {
    .probe = driver2_dummy_probe,
    .remove = driver2_dummy_remove,
    .driver = {
        .name = "driver2_compatible",
    },
};

static int __init driver2_init(void)
{
    int ret;

    printk("[LJW]driver2_init=====>\n");

    ret = platform_device_register(&driver2_dummy_device);
    if (ret < 0) {
        printk("[FAILED]platform_device_register failed for driver2_dummy_device!\n");
        return -1;
    }
    printk("[SUCCESS]platform_device_register for driver2_dummy_device\n");

    ret = platform_driver_register(&driver2_dummy_driver);
    if (ret < 0) {
        printk("[FAILED]platform_driver_register failed for driver2_dummy_driver!\n");
        platform_device_unregister(&driver2_dummy_device);
        return -1;
    }
    printk("[SUCCESS]platform_driver_register for driver2_dummy_driver\n");

    return 0;
}

static void __exit driver2_exit(void)
{
    platform_driver_unregister(&driver2_dummy_driver);
    platform_device_unregister(&driver2_dummy_device);
    return;
}

module_init(driver2_init);
module_exit(driver2_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("liaojunwu");

makefile:

.PHONY: build clean

#Why change the SHELL, because default /bin/sh not support source command
SHELL := /bin/bash
KERNELDIR := /home/liaojunwu/linux/code/sdk_ori_code/sdk_ori

CURRENT_PATH := $(shell pwd)

obj-m := driver1.o
obj-m += driver2.o

build: pre_build kernel_modules

pre_build:
        source /opt/fsl-imx-x11/4.1.15-2.1.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi

kernel_modules:
        $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules

clean:
        $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
        rm -rf *.mod.c *.o *.ko *.order *.symvers

实验结果:

通过实验结果可以很清楚地看到deferred probe的整个过程,可以看到虽然先加载driver2.ko但是driver2的probe是等到driver1的probe执行完毕才执行的,正是因为deferred probe机制为其保证了正确的执行顺序,同时也可以看到driver2的probe实际上是在工作队列中执行的,这时候与driver1的某些代码是并发关系(原始的提交在处理这种并发关系上有一些BUG,后人陆续有修改,具体可以去追溯github),最后在driver2的probe成功后在driver_bound()里面又触发了一次probe的defer。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/768650.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

前端工程中的设计模式应用

本文旨在系统性介绍一下23种设计模式&#xff0c;给出通俗易懂的案例、结构图及代码示例&#xff0c;这也是我自身学习理解的过程。或许其中的几种设计模式写的并不是很清晰明了易懂&#xff0c;更详细的可根据提到的参考文献进行深入学习。 什么是设计模式 设计模式这个概念是…

Python 算法基础篇之字符串操作:索引、切片、常用方法

Python 算法基础篇之字符串操作&#xff1a;索引、切片、常用方法 引言 1. 字符串的概念和创建2. 字符串的索引3. 字符串的切片4. 字符串的常用方法 a ) 查找子字符串 b ) 替换子字符串 c ) 拆分和连接字符串 总结 引言 字符串是一种常见的数据类型&#xff0c;在 Python 中对…

又整新活,新版 IntelliJ IDEA 有点东西!

作为一个经常使用IntelliJ IDEA来写代码的老用户&#xff0c;每次对于JetBrains软件的更新都是非常关注的。 这不最近这段时间&#xff0c;JetBrains连发了多个软件的EAP版本&#xff1a; 同时JetBrains的官博中也宣布了一个重要的新特性&#xff0c;那就是&#xff1a; 在所…

X.509数字证书的基本原理

一、前言 数字证书是现代互联网中个体间相互信任的基石。 如果没有了数字证书&#xff0c;那么也就没有了各式各样的电商平台以及方便的电子支付服务。 数字证书是网络安全中的一个非常重要组成部分。如果要学好网络安全&#xff0c;那么必须充分理解它的原理。 目前我们所…

keepalived 实现双机热备

文章目录 一、说明二、概念解释三、环境准备四、操作过程五、验证 一、说明 我们经常听说 nginx keepalived 双机热备&#xff0c;其实在这里&#xff0c;双机热备只是利用 keepalived 实现两个节点的故障切换&#xff0c;当主节点挂了&#xff0c;备用节点顶上&#xff0c;保…

深入浅出关于网易邮箱开启smtp服务教程

各平台邮箱开启SMTP服务教程 一、QQ邮箱 &#xff08;服务器地址&#xff1a;smtp.qq.com&#xff09; 第一步&#xff1a;复制https://mail.qq.com/ 登录QQ邮箱后电击左上角设置&#xff0c;如图&#xff1a; 第二步&#xff1a;点击进入“帐户”页面 &#xff0c;如图&…

CodeLocator简单使用(AndroidStudio中点击布局元素确认对应view信息)快速接手陌生项目利器

对于陌生项目的一些改动或重构需求时&#xff0c;如果可以在APP点点就能确定知道当前管理哪个activity或fragment必然是省去了很多去代码里搜来搜去的时间。在社群讨论中发现这款AS插件:CodeLocator ,虽然有2年没更新了 今天也试一试看看是否有帮助。 首先下载最新版本的插件&…

通过OSG实现对模型的日照模拟

1. 加载模型 通过OpenSceneGraph加载一个倾斜摄影的场景模型数据&#xff1a; #include <iostream> #include <Windows.h>#include <osgViewer/Viewer> #include <osgDB/ReadFile>using namespace std;int main() {string osgPath "D:/Data/Da…

Appium自动化测试知识点

一、App环境搭建 1、安装jdk&#xff0c;配置jdk环境变量 2、Android SDK环境安装 3、Appium server安装 4、模拟器的安装&#xff08;夜神模拟器&#xff09; 5、安装appium-python-client Python第三方库 二、App自动化测试原理 如何通过代码操作不同操作系统&#xff08;…

基于Dubbo分布式网上售票系统

一、项目介绍 民航售票是一个高度依赖信息业的行业。但在机票销售的管理和规范这方面上存在着很多各种各样的问题。例如订票是客运行业中的一个最基本的业务,表面上看,它只是机票站业务的一个简单的部分,但是它涉及到管理与客户服务等多方面,关系到民航公司能否正常运作。…

log4j--动态打印日志文件到指定文件夹

文章目录 log4j--动态打印日志文件到指定文件夹1、添加Maven依赖2、配置文件 log4j.properties3、编写日志打印工具类 LogUtil4、工具类调用 log4j–动态打印日志文件到指定文件夹 1、添加Maven依赖 <!-- log4j日志相关坐标 --><dependency><groupId>org.s…

无虚拟 DOM 版 Vue 进行到哪一步了?

前言 就在一年前的 Vue Conf 2022&#xff0c;尤雨溪向大家分享了一个非常令人期待的新模式&#xff1a;无虚拟 DOM 模式&#xff01; 我看了回放之后非常兴奋&#xff0c;感觉这是个非常牛逼的新 feature&#xff0c;鉴于可能会有部分人还不知道或者还没听过什么是 Vue 无虚…

哈佛“聘请”AI担任导师,主讲教授:别全信它的,学生应“批判性地思考”

就在人们为AI聊天机器人的利弊争论不休时&#xff0c;哈佛宣布了一个重磅决定&#xff1a;将利用类似ChatGPT的聊天机器人来帮助授课了。 负责的还是计算机系的旗舰项目 —— 计算机科学导论&#xff0c;也就是著名的 CS50。借助机器人导师&#xff0c;哈佛的 CS50 项目将拥有…

Python、Selenium实现问卷星自动填写(内含适配个人问卷的方法)

&#x1f9d1;‍&#x1f4bb;作者名称&#xff1a;DaenCode &#x1f3a4;作者简介&#xff1a;啥技术都喜欢捣鼓捣鼓&#xff0c;喜欢分享技术、经验、生活。 &#x1f60e;人生感悟&#xff1a;尝尽人生百味&#xff0c;方知世间冷暖。 &#x1f4d6;所属专栏&#xff1a;Py…

有哪些好用的远程传输大文件的软件

随着网络技术的日益进步和普及&#xff0c;大文件远程传输已经成为了人们生活和工作中必不可少的一部分。然而&#xff0c;在远程传输大文件的过程中&#xff0c;经常会遇到传输速度慢、容易受到干扰等问题&#xff0c;因此需要一款高效、稳定、安全的大文件远程传输软件来解决…

CCF真题练习:202209-1如此编码

题目背景 某次测验后&#xff0c;顿顿老师在黑板上留下了一串数字 23333 便飘然而去。凝望着这个神秘数字&#xff0c;小 P 同学不禁陷入了沉思…… 题目描述 已知某次测验包含 n 道单项选择题&#xff0c;其中第 i 题&#xff08;1≤i≤n&#xff09;有 个选项&#xff0c;…

【Vue 面试题10道】我好像之前想过要写,不过之前JavaScript面试题比较多,就暂时略过了,这些应该几乎把常问的都包括了

博主&#xff1a;_LJaXi Or 東方幻想郷 专栏&#xff1a; 前端面试题 开发工具&#xff1a;Vs Code 本题针对 Vue2 这些几乎把常用的都包括了&#xff0c;问别的就没意思了&#xff0c;毕竟工作拧螺丝嘛 我都好久不用Vue了&#xff0c;不过用了React再回看Vue感觉好简单啊… 其…

数据库性能优化中的查询优化

数据库性能优化中的查询优化 概述 在数据库应用中&#xff0c;查询操作是最常见的操作之一。查询优化是数据库性能优化的关键一环&#xff0c;通过对查询语句和查询执行计划的优化&#xff0c;可以显著提高数据库系统的性能和效率。本文将介绍查询优化的相关知识&#xff0c;…

结构型模式 - 代理模式

概述 由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时&#xff0c;访问对象不适合或者不能直接引用目标对象&#xff0c;代理对象作为访问对象和目标对象之间的中介。 Java中的代理按照代理类生成时机不同又分为静态代理和动态代理。静态代理代理类在编译期…

Docker 基础知识解析:容器与传统虚拟化对比:资源利用、启动时间、隔离性和部署效率

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…