作者
彭东林
pengdonglin137@163.com
平台
Linux-4.10.17
Qemu2.8 + vexpress-a9
概述
在系统开机probe驱动的时候,有些设备驱动加载可能需要比较长的时间,尤其是像i2c这样的设备,总线速率较低,如果在probe时读写大量的寄存器的话,会使系统的开机速度变长。针对这个问题,Linux内核提供了驱动异步的加载机制,当然在使用时也有一些限制,比如挂载同一条总线下的设备驱动加载只能串行等等,此外,如果设备的驱动之间存在依赖关系,那么也要慎用。
下面是Linux内核里引入驱动异步加载的几个patch:
765230b5f084863183aa8adb3405ab3f32c0b16e
f2411da746985e60d4d087f3a43e271c61785927
d173a137c5bd95ee29d02705e5fa8890ef149718
引用提交记录中对设备驱动异步加载的解释:
Some devices take a long time when initializing, and not all drivers are suited to initialize their devices when they are open. For example, input drivers need to interrogate their devices in order to publish device's capabilities before userspace will open them. When such drivers are compiled into kernel they may stall entire kernel initialization. This change allows drivers request for their probe functions to be called asynchronously during driver and device registration (manual binding is still synchronous). Because async_schedule is used to perform asynchronous calls module loading will still wait for the probing to complete. Note that the end goal is to make the probing asynchronous by default, so annotating drivers with PROBE_PREFER_ASYNCHRONOUS is a temporary measure that allows us to speed up boot process while we validating and fixing the rest of the drivers and preparing userspace.
下面用两个示例说明一下,一个是基于platform的,另一个是基于i2c的。
涉及到代码可以到下面的链接下载:https://github.com/pengdonglin137/async_drv_load_demo
正文
一、I2C从设备驱动的异步加载
这里只关心调用驱动probe的顺序,不关心具体驱动的功能。在设备树里添加4个I2C控制器节点,在第1组I2C控制器下挂两个从设备,目的是观察同一条I2C总线下的从设备驱动的加载顺序,其他三个I2C控制器下面各挂一个从设备,目的是观察不同I2C总线下的从设备驱动的异步加载顺序。为了方便,在驱动probe函数中添加了msleep函数。
由于在下面的例子中,设备树的解析时间较早,也就是在注册驱动时,对应的device早已经存在了,所以主要分析注册驱动后,驱动找设备的情形。
-
设备树
I2C控制器1,下面挂了两个从设备:
async_demo1_i2c: i2c@20000 { compatible = "arm,versatile-i2c"; reg = <0x20000 0x1000>; #address-cells = <1>; #size-cells = <0>; async_demo1@39 { compatible = "async_demo1_i2c"; reg = <0x39>; status = "okay"; }; async_demo1@3a { compatible = "async_demo5_i2c"; reg = <0x3a>; status = "okay"; }; };
其他三个I2C控制器,每个下面各挂一个从设备:
async_demo2_i2c: i2c@30000 { compatible = "arm,versatile-i2c"; reg = <0x30000 0x1000>; #address-cells = <1>; #size-cells = <0>; async_demo2@39 { compatible = "async_demo2_i2c"; reg = <0x39>; status = "okay"; }; }; async_demo3_i2c: i2c@40000 { compatible = "arm,versatile-i2c"; reg = <0x40000 0x1000>; #address-cells = <1>; #size-cells = <0>; async_demo3@39 { compatible = "async_demo3_i2c"; reg = <0x39>; status = "okay"; }; }; async_demo4_i2c: i2c@50000 { compatible = "arm,versatile-i2c"; reg = <0x50000 0x1000>; #address-cells = <1>; #size-cells = <0>; async_demo4@39 { compatible = "async_demo4_i2c"; reg = <0x39>; status = "okay"; }; };
上面是三个I2C控制器,具体每个I2C控制的reg属性是随意指定的,只要跟其他设备不冲突就行,因为这里只关心驱动的probe。
-
设备驱动
设备1对应的驱动probe中睡眠4s,并probe_type为PROBE_PREFER_ASYNCHRONOUS:
1 static int async_demo1_probe(struct i2c_client *i2c, 2 const struct i2c_device_id *id) 3 { 4 printk("%s enter ", __func__); 5 msleep(4000); 6 printk("%s exit ", __func__); 7 return 0; 8 } 9 10 static struct i2c_driver async_demo1_i2c_driver = { 11 .probe = async_demo1_probe, 12 .remove = async_demo1_remove, 13 .id_table = async_demo1_i2c_id, 14 .driver = { 15 .name = "async_demo1_i2c", 16 .of_match_table = async_demo1_i2c_dt_ids, 17 #ifdef USE_ASYNC 18 .probe_type = PROBE_PREFER_ASYNCHRONOUS, 19 #else 20 .probe_type = PROBE_FORCE_SYNCHRONOUS, 21 #endif 22 }, 23 };
其他几个设备2、3、4、5的驱动中将probe_type也设置为PROBE_PREFER_ASYNCHRONOUS,在probe函数中分别延迟3s、2s、1s和100ms。
为了对比,用宏USE_ASYNC来控制是否驱动是否支持异步加载,如果没有定义,那么probe_type就是PROBE_FORCE_SYNCHRONOUS,表示使用同步加载。否则设置为PROBE_PREFER_ASYNCHRONOUS,即使用异步加载。
-
验证
先看看同步加载驱动的耗时:
1 [root@vexpress mnt]# insmod async_demo_i2c.ko 2 [ 5099.103927] [ 1| 871| insmod] async_demo_i2c_init enter. 3 [ 5099.104108] [ 1| 871| insmod] before async_demo1_i2c 4 [ 5099.104312] [ 1| 871| insmod] __driver_attach enter, dev: 2-0039 5 [ 5099.104466] [ 1| 871| insmod] bus: 'i2c': driver_probe_device: matched device 2-0039 with driver async_demo1_i2c 6 [ 5099.104798] [ 1| 871| insmod] async_demo1_probe enter 7 [ 5102.861699] [ 1| 871| insmod] async_demo1_probe exit 8 [ 5102.862238] [ 1| 871| insmod] bus_add_driver exit. 9 [ 5102.862465] [ 1| 871| insmod] before async_demo2_i2c 10 [ 5102.862663] [ 1| 871| insmod] __driver_attach enter, dev: 3-0039 11 [ 5102.862794] [ 1| 871| insmod] bus: 'i2c': driver_probe_device: matched device 3-0039 with driver async_demo2_i2c 12 [ 5102.863064] [ 1| 871| insmod] async_demo2_probe enter 13 [ 5105.706465] [ 1| 871| insmod] async_demo2_probe exit 14 [ 5105.707067] [ 1| 871| insmod] bus_add_driver exit. 15 [ 5105.707317] [ 1| 871| insmod] before async_demo3_i2c 16 [ 5105.707516] [ 1| 871| insmod] __driver_attach enter, dev: 4-0039 17 [ 5105.707652] [ 1| 871| insmod] bus: 'i2c': driver_probe_device: matched device 4-0039 with driver async_demo3_i2c 18 [ 5105.707939] [ 1| 871| insmod] async_demo3_probe enter 19 [ 5107.652718] [ 1| 871| insmod] async_demo3_probe exit 20 [ 5107.653142] [ 1| 871| insmod] bus_add_driver exit. 21 [ 5107.653398] [ 1| 871| insmod] before async_demo4_i2c 22 [ 5107.653653] [ 1| 871| insmod] __driver_attach enter, dev: 5-0039 23 [ 5107.653852] [ 1| 871| insmod] bus: 'i2c': driver_probe_device: matched device 5-0039 with driver async_demo4_i2c 24 [ 5107.654243] [ 1| 871| insmod] async_demo4_probe enter 25 [ 5108.625550] [ 1| 871| insmod] async_demo4_probe exit 26 [ 5108.626019] [ 1| 871| insmod] bus_add_driver exit. 27 [ 5108.626219] [ 1| 871| insmod] before async_demo5_i2c 28 [ 5108.626460] [ 1| 871| insmod] __driver_attach enter, dev: 2-003a 29 [ 5108.626604] [ 1| 871| insmod] bus: 'i2c': driver_probe_device: matched device 2-003a with driver async_demo5_i2c 30 [ 5108.626896] [ 1| 871| insmod] async_demo5_probe enter 31 [ 5108.738170] [ 1| 871| insmod] async_demo5_probe exit 32 [ 5108.738635] [ 1| 871| insmod] bus_add_driver exit.
可以看到,从开始加载到最后加载完毕,耗时9.6s左右,也就是每个驱动probe耗时的累加。上面的驱动的probe被调用的顺序跟驱动的注册顺序相同,先注册的先被调用,串行进行,而且自始至总都在insmod这一个进程的上下文中。
再看看异步加载的情况:
1 [root@vexpress mnt]# insmod async_demo_i2c.ko 2 [ 5487.522979] [ 2| 886| insmod] async_demo_i2c_init enter. 3 [ 5487.523168] [ 2| 886| insmod] before async_demo1_i2c 4 [ 5487.523379] [ 2| 886| insmod] bus: 'i2c': probing driver async_demo1_i2c asynchronously 5 [ 5487.523817] [ 2| 886| insmod] bus_add_driver exit. 6 [ 5487.524034] [ 2| 886| insmod] before async_demo2_i2c 7 [ 5487.524227] [ 2| 886| insmod] bus: 'i2c': probing driver async_demo2_i2c asynchronously 8 [ 5487.524545] [ 2| 886| insmod] bus_add_driver exit. 9 [ 5487.524759] [ 2| 886| insmod] before async_demo3_i2c 10 [ 5487.524949] [ 2| 886| insmod] bus: 'i2c': probing driver async_demo3_i2c asynchronously 11 [ 5487.525248] [ 2| 886| insmod] bus_add_driver exit. 12 [ 5487.525448] [ 2| 886| insmod] before async_demo4_i2c 13 [ 5487.525642] [ 2| 886| insmod] bus: 'i2c': probing driver async_demo4_i2c asynchronously 14 [ 5487.525951] [ 2| 886| insmod] bus_add_driver exit. 15 [ 5487.526177] [ 2| 886| insmod] before async_demo5_i2c 16 [ 5487.526421] [ 2| 886| insmod] bus: 'i2c': probing driver async_demo5_i2c asynchronously 17 [ 5487.526728] [ 2| 886| insmod] bus_add_driver exit. 18 [ 5487.527397] [ 0| 858| kworker/u8:3] driver_attach_async enter. 19 [ 5487.527627] [ 0| 858| kworker/u8:3] __driver_attach enter, dev: 2-0039 20 [ 5487.527822] [ 0| 858| kworker/u8:3] bus: 'i2c': driver_probe_device: matched device 2-0039 with driver async_demo1_i2c 21 [ 5487.528246] [ 0| 858| kworker/u8:3] async_demo1_probe enter 22 [ 5487.531002] [ 0| 735| kworker/u8:4] driver_attach_async enter. 23 [ 5487.531236] [ 0| 735| kworker/u8:4] __driver_attach enter, dev: 3-0039 24 [ 5487.531431] [ 0| 735| kworker/u8:4] bus: 'i2c': driver_probe_device: matched device 3-0039 with driver async_demo2_i2c 25 [ 5487.532741] [ 3| 888| kworker/u8:0] driver_attach_async enter. 26 [ 5487.532968] [ 3| 888| kworker/u8:0] __driver_attach enter, dev: 4-0039 27 [ 5487.533162] [ 3| 888| kworker/u8:0] bus: 'i2c': driver_probe_device: matched device 4-0039 with driver async_demo3_i2c 28 [ 5487.533647] [ 3| 888| kworker/u8:0] async_demo3_probe enter 29 [ 5487.534076] [ 3| 890| kworker/u8:1] driver_attach_async enter. 30 [ 5487.534291] [ 3| 890| kworker/u8:1] __driver_attach enter, dev: 5-0039 31 [ 5487.534505] [ 3| 890| kworker/u8:1] bus: 'i2c': driver_probe_device: matched device 5-0039 with driver async_demo4_i2c 32 [ 5487.534865] [ 3| 890| kworker/u8:1] async_demo4_probe enter 33 [ 5487.535282] [ 3| 892| kworker/u8:2] driver_attach_async enter. 34 [ 5487.535488] [ 3| 892| kworker/u8:2] __driver_attach enter, dev: 2-003a 35 [ 5487.539944] [ 0| 735| kworker/u8:4] async_demo2_probe enter 36 [ 5488.514837] [ 3| 890| kworker/u8:1] async_demo4_probe exit 37 [ 5488.515209] [ 3| 890| kworker/u8:1] bus: 'i2c': driver async_demo4_i2c async attach completed: 0 38 [ 5489.488056] [ 3| 888| kworker/u8:0] async_demo3_probe exit 39 [ 5489.488394] [ 3| 888| kworker/u8:0] bus: 'i2c': driver async_demo3_i2c async attach completed: 0 40 [ 5490.384618] [ 0| 735| kworker/u8:4] async_demo2_probe exit 41 [ 5490.384894] [ 0| 735| kworker/u8:4] bus: 'i2c': driver async_demo2_i2c async attach completed: 0 42 [ 5491.282840] [ 0| 858| kworker/u8:3] async_demo1_probe exit 43 [ 5491.283172] [ 0| 858| kworker/u8:3] bus: 'i2c': driver async_demo1_i2c async attach completed: 0 44 [ 5491.283724] [ 3| 892| kworker/u8:2] bus: 'i2c': driver_probe_device: matched device 2-003a with driver async_demo5_i2c 45 [ 5491.284663] [ 3| 892| kworker/u8:2] async_demo5_probe enter 46 [ 5491.396770] [ 3| 892| kworker/u8:2] async_demo5_probe exit 47 [ 5491.397074] [ 3| 892| kworker/u8:2] bus: 'i2c': driver async_demo5_i2c async attach completed: 0
可以看到,驱动加载耗时3.9s左右,其实也就是耗时probe耗时最长的那个驱动的时间,看到异步加载的强大了吧。此外,由于demo5和demo1挂载在一个I2C控制器下,即便驱动里支持异步,但是从结果来看还是是串行的。上面第二个中括号中数字的含义 [处理器编号 | 进程PID | 进程名称]。
在异步加载情形下,驱动注册时,先在insmod进程的上下文,然后发现可以支持异步,就会启动一个后台内核线程来负责接下来的加载任务,上面先后出现了5个内核线程。上面的kworker/u8:n都是内核线程,属于内核工作队列的知识,关于内核工作队列可以阅读笨叔叔的《奔跑吧Linux内核》的5.3节,顺便学习一个笨叔叔在书中传授的关于Linux内核里CMWQ的一个知识点:
CMWQ机制会动态地调整一个线程池中工作线程的执行情况,不会因为某个work回调函数执行了阻塞操作而影响整个线程池中其他work的执行。
上面的例子也充分说明了这一点。
二、platform设备驱动的异步加载
对于platform总线上的设备来说有些特殊,即便驱动里将probe_type设置为了PROBE_PREFER_ASYNCHRONOUS,但是如果先注册device,再注册driver的话还是串行进行,原因是driver在attach到device时,会先device_lock(dev->parent),然后再去probe。对于platform总线上的device,其dev->parent都指向platform_bus,所以虽然从log上看,确实是由内核线程负责加载驱动的,但是由于锁的原因,表现出来的还是串行,这一点我觉得应该可以优化。如果是先注册driver,后注册device的话,是可以异步的,原因是没有调用device_lock(dev->parent)。
1、device找driver的情形
跟前面类似,有四个platform_driver,第1个platform_driver的probe里睡4s,第2、3和4个分别睡3s、2s和1s。在驱动模块init时,先注册platform_driver,再注册platform_device。下面是部分示例驱动:
1 static struct platform_driver async_demo4_v2_driver = { 2 .probe = async_demo4_v2_probe, 3 .remove = async_demo4_v2_remove, 4 .driver = { 5 .name = "async_demo4_v2", 6 .of_match_table = of_match_ptr(async_demo4_v2_dt_ids), 7 #ifdef USE_ASYNC 8 .probe_type = PROBE_PREFER_ASYNCHRONOUS, 9 #else 10 .probe_type = PROBE_FORCE_SYNCHRONOUS, 11 #endif 12 }, 13 }; 14 15 static struct platform_device *pdev[4]; 16 17 static __init int async_demo_init(void) 18 { 19 printk("%s enter. ", __func__); 20 21 printk("Register Platform Driver "); 22 platform_driver_register(&async_demo1_v2_driver); 23 platform_driver_register(&async_demo2_v2_driver); 24 platform_driver_register(&async_demo3_v2_driver); 25 platform_driver_register(&async_demo4_v2_driver); 26 27 printk(" Register Platform Device "); 28 pdev[0] = platform_device_register_simple("async_demo1_v2", 0, NULL, 0); 29 pdev[1] = platform_device_register_simple("async_demo2_v2", 0, NULL, 0); 30 pdev[2] = platform_device_register_simple("async_demo3_v2", 0, NULL, 0); 31 pdev[3] = platform_device_register_simple("async_demo4_v2", 0, NULL, 0); 32 33 return 0; 34 }
先看同步加载的耗时:
1 [root@vexpress mnt]# insmod async_demo_v2.ko 2 [ 49.125194] [ 2| 830| insmod] async_demo_init enter. 3 [ 49.125382] [ 2| 830| insmod] Register Platform Driver 4 [ 49.125834] [ 2| 830| insmod] bus_add_driver exit. 5 [ 49.126256] [ 2| 830| insmod] bus_add_driver exit. 6 [ 49.126665] [ 2| 830| insmod] bus_add_driver exit. 7 [ 49.127065] [ 2| 830| insmod] bus_add_driver exit. 8 [ 49.127218] [ 2| 830| insmod] 9 [ 49.127218] [ 2| 830| insmod] 10 [ 49.127218] [ 2| 830| insmod] Register Platform Device 11 [ 49.127760] [ 2| 830| insmod] bus: 'platform': driver_probe_device: matched device async_demo1_v2.0 with driver async_demo1_v2 12 [ 49.128085] [ 2| 830| insmod] async_demo1_v2_probe: parent platform 13 [ 49.128232] [ 2| 830| insmod] async_demo1_v2_probe enter. 14 [ 52.900810] [ 2| 830| insmod] async_demo1_v2_probe exit. 15 [ 52.901450] [ 2| 830| insmod] bus: 'platform': driver_probe_device: matched device async_demo2_v2.0 with driver async_demo2_v2 16 [ 52.901819] [ 2| 830| insmod] async_demo2_v2_probe: parent platform 17 [ 52.902015] [ 2| 830| insmod] async_demo2_v2_probe enter. 18 [ 55.750675] [ 2| 830| insmod] async_demo2_v2_probe exit. 19 [ 55.751344] [ 2| 830| insmod] bus: 'platform': driver_probe_device: matched device async_demo3_v2.0 with driver async_demo3_v2 20 [ 55.751631] [ 2| 830| insmod] async_demo3_v2_probe enter. 21 [ 57.700598] [ 2| 830| insmod] async_demo3_v2_probe exit. 22 [ 57.701227] [ 2| 830| insmod] bus: 'platform': driver_probe_device: matched device async_demo4_v2.0 with driver async_demo4_v2 23 [ 57.701509] [ 2| 830| insmod] async_demo4_v2_probe enter. 24 [ 58.675576] [ 2| 830| insmod] async_demo4_v2_probe exit.
耗时9.5s左右
看看异步的耗时:
1 [root@vexpress mnt]# insmod async_demo_v2.ko 2 [ 360.852433] [ 1| 878| insmod] async_demo_init enter. 3 [ 360.852618] [ 1| 878| insmod] Register Platform Driver 4 [ 360.852808] [ 1| 878| insmod] bus: 'platform': probing driver async_demo1_v2 asynchronously 5 [ 360.853108] [ 1| 878| insmod] bus_add_driver exit. 6 [ 360.853327] [ 1| 878| insmod] bus: 'platform': probing driver async_demo2_v2 asynchronously 7 [ 360.853608] [ 1| 878| insmod] bus_add_driver exit. 8 [ 360.853831] [ 1| 878| insmod] bus: 'platform': probing driver async_demo3_v2 asynchronously 9 [ 360.854090] [ 1| 878| insmod] bus_add_driver exit. 10 [ 360.854287] [ 1| 878| insmod] bus: 'platform': probing driver async_demo4_v2 asynchronously 11 [ 360.854568] [ 1| 878| insmod] bus_add_driver exit. 12 [ 360.854727] [ 1| 878| insmod] 13 [ 360.854727] [ 1| 878| insmod] 14 [ 360.854727] [ 1| 878| insmod] Register Platform Device 15 [ 360.855376] [ 1| 878| insmod] platform async_demo1_v2.0: scheduling asynchronous probe 16 [ 360.855835] [ 1| 878| insmod] platform async_demo2_v2.0: scheduling asynchronous probe 17 [ 360.856274] [ 1| 878| insmod] platform async_demo3_v2.0: scheduling asynchronous probe 18 [ 360.857174] [ 0| 852| kworker/u8:0] driver_attach_async enter. 19 [ 360.857537] [ 0| 852| kworker/u8:0] __driver_attach enter, dev: async_demo1_v2.0 20 [ 360.857712] [ 0| 852| kworker/u8:0] bus: 'platform': driver_probe_device: matched device async_demo1_v2.0 with driver async_demo1_v2 21 [ 360.858984] [ 0| 852| kworker/u8:0] async_demo1_v2_probe: parent platform 22 [ 360.859267] [ 0| 852| kworker/u8:0] async_demo1_v2_probe enter. 23 [ 360.859571] [ 0| 854| kworker/u8:1] driver_attach_async enter. 24 [ 360.859876] [ 0| 854| kworker/u8:1] __driver_attach enter, dev: async_demo2_v2.0 25 [ 360.860130] [ 0| 50| kworker/u8:2] driver_attach_async enter. 26 [ 360.860412] [ 0| 50| kworker/u8:2] __driver_attach enter, dev: async_demo3_v2.0 27 [ 360.866237] [ 1| 878| insmod] platform async_demo4_v2.0: scheduling asynchronous probe 28 [ 360.866985] [ 2| 856| kworker/u8:3] driver_attach_async enter. 29 [ 360.867361] [ 2| 856| kworker/u8:3] __driver_attach enter, dev: async_demo4_v2.0 30 [ 360.868341] [ 3| 858| kworker/u8:5] __device_attach_async_helper enter. 31 [ 360.869812] [ 1| 292| kworker/u8:4] __device_attach_async_helper enter. 32 [ 360.870003] [ 1| 292| kworker/u8:4] bus: 'platform': driver_probe_device: matched device async_demo2_v2.0 with driver async_demo2_v2 33 [ 360.870296] [ 1| 292| kworker/u8:4] async_demo2_v2_probe: parent platform 34 [ 360.870423] [ 1| 292| kworker/u8:4] async_demo2_v2_probe enter. 35 [ 360.870882] [ 1| 863| kworker/u8:7] __device_attach_async_helper enter. 36 [ 360.871057] [ 1| 863| kworker/u8:7] bus: 'platform': driver_probe_device: matched device async_demo3_v2.0 with driver async_demo3_v2 37 [ 360.871365] [ 1| 863| kworker/u8:7] async_demo3_v2_probe enter. 38 [ 360.897155] [ 3| 861| kworker/u8:6] __device_attach_async_helper enter. 39 [ 360.897380] [ 3| 861| kworker/u8:6] bus: 'platform': driver_probe_device: matched device async_demo4_v2.0 with driver async_demo4_v2 40 [ 360.897688] [ 3| 861| kworker/u8:6] async_demo4_v2_probe enter. 41 [ 361.890088] [ 3| 861| kworker/u8:6] async_demo4_v2_probe exit. 42 [ 361.890527] [ 3| 861| kworker/u8:6] async_demo4_v2 async_demo4_v2.0: async probe completed 43 [ 362.789551] [ 1| 863| kworker/u8:7] async_demo3_v2_probe exit. 44 [ 362.789885] [ 1| 863| kworker/u8:7] async_demo3_v2 async_demo3_v2.0: async probe completed 45 [ 363.764507] [ 1| 292| kworker/u8:4] async_demo2_v2_probe exit. 46 [ 363.764866] [ 1| 292| kworker/u8:4] async_demo2_v2 async_demo2_v2.0: async probe completed 47 [ 364.662938] [ 0| 852| kworker/u8:0] async_demo1_v2_probe exit. 48 [ 364.663276] [ 0| 852| kworker/u8:0] bus: 'platform': driver async_demo1_v2 async attach completed: 0 49 [ 364.663581] [ 0| 854| kworker/u8:1] bus: 'platform': driver async_demo2_v2 async attach completed: 0 50 [ 364.663845] [ 0| 50| kworker/u8:2] bus: 'platform': driver async_demo3_v2 async attach completed: 0 51 [ 364.664372] [ 3| 858| kworker/u8:5] async_demo1_v2 async_demo1_v2.0: async probe completed 52 [ 364.664692] [ 2| 856| kworker/u8:3] bus: 'platform': driver async_demo4_v2 async attach completed: 0
耗时3.8s左右。
2、driver找device的情形
对于这种情况,只能改一下驱动,修改driver/base/dd.c:
1 @@ -749,13 +752,17 @@ static int __driver_attach(struct device *dev, void *data) 2 return ret; 3 } /* ret > 0 means positive match */ 4 5 - if (dev->parent) /* Needed for USB */ 6 + printk("%s enter, dev: %s ", __func__, dev_name(dev)); 7 + 8 + if (dev->parent && (dev->parent != &platform_bus)) /* Needed for USB */ 9 device_lock(dev->parent); 10 + 11 device_lock(dev); 12 if (!dev->driver) 13 driver_probe_device(drv, dev); 14 device_unlock(dev); 15 - if (dev->parent) 16 + 17 + if (dev->parent && (dev->parent != &platform_bus)) /* Needed for USB */ 18 device_unlock(dev->parent); 19 20 return 0;
也就是,当发现parent是platform_bus时,不调用device_lock。
下面看一下同步的耗时:
1 [root@vexpress mnt]# insmod async_demo.ko 2 [ 21.384197] [ 1| 809| insmod] async_demo: loading out-of-tree module taints kernel. 3 [ 21.389316] [ 1| 809| insmod] async_demo_init enter. 4 [ 21.389462] [ 1| 809| insmod] before async_demo1 5 [ 21.389829] [ 1| 809| insmod] __driver_attach enter, dev: async_demo1 6 [ 21.389978] [ 1| 809| insmod] bus: 'platform': driver_probe_device: matched device async_demo1 with driver async_demo1 7 [ 21.390302] [ 1| 809| insmod] async_demo1_probe: parent platform 8 [ 21.390479] [ 1| 809| insmod] async_demo1_probe enter. 9 [ 25.161960] [ 1| 809| insmod] async_demo1_probe exit. 10 [ 25.162396] [ 1| 809| insmod] bus_add_driver exit. 11 [ 25.162583] [ 1| 809| insmod] before async_demo2 12 [ 25.162941] [ 1| 809| insmod] __driver_attach enter, dev: async_demo2 13 [ 25.163091] [ 1| 809| insmod] bus: 'platform': driver_probe_device: matched device async_demo2 with driver async_demo2 14 [ 25.163374] [ 1| 809| insmod] async_demo2_probe: parent platform 15 [ 25.163517] [ 1| 809| insmod] async_demo2_probe enter. 16 [ 28.017117] [ 1| 809| insmod] async_demo2_probe exit. 17 [ 28.017622] [ 1| 809| insmod] bus_add_driver exit. 18 [ 28.017863] [ 1| 809| insmod] before async_demo3 19 [ 28.018244] [ 1| 809| insmod] __driver_attach enter, dev: async_demo3 20 [ 28.018419] [ 1| 809| insmod] bus: 'platform': driver_probe_device: matched device async_demo3 with driver async_demo3 21 [ 28.018736] [ 1| 809| insmod] async_demo3_probe enter. 22 [ 29.970867] [ 1| 809| insmod] async_demo3_probe exit. 23 [ 29.971283] [ 1| 809| insmod] bus_add_driver exit. 24 [ 29.971509] [ 1| 809| insmod] before async_demo4 25 [ 29.971893] [ 1| 809| insmod] __driver_attach enter, dev: async_demo4 26 [ 29.972048] [ 1| 809| insmod] bus: 'platform': driver_probe_device: matched device async_demo4 with driver async_demo4 27 [ 29.972341] [ 1| 809| insmod] async_demo4_probe enter. 28 [ 30.947713] [ 1| 809| insmod] async_demo4_probe exit. 29 [ 30.948153] [ 1| 809| insmod] bus_add_driver exit.
耗时9.6s左右
下面是异步耗时:
1 [ 129.732599] [ 1| 822| insmod] async_demo_init enter. 2 [ 129.732765] [ 1| 822| insmod] before async_demo1 3 [ 129.732945] [ 1| 822| insmod] bus: 'platform': probing driver async_demo1 asynchronously 4 [ 129.733311] [ 1| 822| insmod] bus_add_driver exit. 5 [ 129.733461] [ 1| 822| insmod] before async_demo2 6 [ 129.733611] [ 1| 822| insmod] bus: 'platform': probing driver async_demo2 asynchronously 7 [ 129.733858] [ 1| 822| insmod] bus_add_driver exit. 8 [ 129.734004] [ 1| 822| insmod] before async_demo3 9 [ 129.734155] [ 1| 822| insmod] bus: 'platform': probing driver async_demo3 asynchronously 10 [ 129.734385] [ 1| 822| insmod] bus_add_driver exit. 11 [ 129.734524] [ 1| 822| insmod] before async_demo4 12 [ 129.734676] [ 1| 822| insmod] bus: 'platform': probing driver async_demo4 asynchronously 13 [ 129.734909] [ 1| 822| insmod] bus_add_driver exit. 14 [ 129.735450] [ 3| 403| kworker/u8:4] driver_attach_async enter. 15 [ 129.735772] [ 3| 403| kworker/u8:4] __driver_attach enter, dev: async_demo1 16 [ 129.736095] [ 3| 403| kworker/u8:4] bus: 'platform': driver_probe_device: matched device async_demo1 with driver async_demo1 17 [ 129.736430] [ 3| 403| kworker/u8:4] async_demo1_probe: parent platform 18 [ 129.736570] [ 3| 403| kworker/u8:4] async_demo1_probe enter. 19 [ 129.736818] [ 3| 262| kworker/u8:3] driver_attach_async enter. 20 [ 129.737088] [ 3| 262| kworker/u8:3] __driver_attach enter, dev: async_demo2 21 [ 129.737231] [ 3| 262| kworker/u8:3] bus: 'platform': driver_probe_device: matched device async_demo2 with driver async_demo2 22 [ 129.737496] [ 3| 262| kworker/u8:3] async_demo2_probe: parent platform 23 [ 129.737633] [ 3| 262| kworker/u8:3] async_demo2_probe enter. 24 [ 129.745080] [ 2| 35| kworker/u8:2] driver_attach_async enter. 25 [ 129.752320] [ 3| 28| kworker/u8:1] driver_attach_async enter. 26 [ 129.764287] [ 2| 35| kworker/u8:2] __driver_attach enter, dev: async_demo3 27 [ 129.764487] [ 2| 35| kworker/u8:2] bus: 'platform': driver_probe_device: matched device async_demo3 with driver async_demo3 28 [ 129.764807] [ 2| 35| kworker/u8:2] async_demo3_probe enter. 29 [ 129.765317] [ 3| 28| kworker/u8:1] __driver_attach enter, dev: async_demo4 30 [ 129.765465] [ 3| 28| kworker/u8:1] bus: 'platform': driver_probe_device: matched device async_demo4 with driver async_demo4 31 [ 129.765760] [ 3| 28| kworker/u8:1] async_demo4_probe enter. 32 [ 130.733864] [ 3| 28| kworker/u8:1] async_demo4_probe exit. 33 [ 130.734143] [ 3| 28| kworker/u8:1] bus: 'platform': driver async_demo4 async attach completed: 0 34 [ 131.710647] [ 2| 35| kworker/u8:2] async_demo3_probe exit. 35 [ 131.710940] [ 2| 35| kworker/u8:2] bus: 'platform': driver async_demo3 async attach completed: 0 36 [ 132.612441] [ 3| 262| kworker/u8:3] async_demo2_probe exit. 37 [ 132.612759] [ 3| 262| kworker/u8:3] bus: 'platform': driver async_demo2 async attach completed: 0 38 [ 133.514075] [ 3| 403| kworker/u8:4] async_demo1_probe exit. 39 [ 133.514386] [ 3| 403| kworker/u8:4] bus: 'platform': driver async_demo1 async attach completed: 0
耗时3.8s左右。
未完待续……