device link

文章:kernel.org/doc/html/latest/driver-api/device_link.html

device link

默认情况下,驱动core仅在设备层次结构中父子设备间建立依赖关系:在休眠或唤醒过程中,设备依次按设备之间的关系执行,比如子设备必须在父设备前休眠,而父设备必须在子设备钱唤醒

除了父子关系外,有时需要在设备间如兄弟设备建立依赖,并且驱动core能够自动处理这种关系。另外默认情况下驱动core不会强制执行任何驱动存在性依赖,即一个设备必须绑定到一个驱动程序,另一个设备才能正常探测和运行。

通常这两种依赖结合在一起,一个设备依赖另一个设备,不仅在驱动是否存在,而且在休眠唤醒顺序上。

通常device link可以结合这两种类型依赖:它在supplier设备和consumer设备之间保证休眠唤醒顺序,同时也保证supplier侧驱动存在。consumer设备在supplier设备绑定到驱动上之前是不会probe的,在supplier设备不绑定时consumer设备也是不绑定的。

当supplier侧驱动是否存在不重要,只需要suspend/resume/shutdown顺序时,device link可以只设置标志DL_FLAG_STATELESS. 换句话,supplier侧的驱动是否存在是可选的。

另外可选的特性是runtime PM集成:对device link设置DL_FLAG_PM_RUNTIME,PM core 会运行时唤醒supplier, 并保持active只要consumer在运行唤醒状态时。

Usage

可添加device link最早的地方是在supplier设备调用device_add()后和在consumer设备在调用device_initialize()后。也可以稍后添加,但必须注意保持系统一致性状态:如device link不能再suspend/resume转换中添加,因此需要用lock_system_sleep()阻止这种转化,或者通过能够保证不会并行的suspend/resume转换的函数来添加device link,比如设备的probe()回调或启动时PCI quirk。

另一个不一致状态的例子是代表驱动存在性依赖的device link, 它从consumer的probe回调被添加但此时supplier设备却没有开始probe,驱动core比较早的知道device link,但它不会在第一时间probe起来consumer。在添加device link后,consumer有义务检查supplier的存在性,若不存在就延迟probe。
NOTE:在supplier设备probe过程中可以从consumer设备的probe回调函数中创建device link是有效的,但consumer必须知道在link生成时supplier是可用的。

若在supplier或consumer设备的probe回调函数里添加DL_FLAG_STATELESS时,同样在remove回调函数里删除。如果驱动编译成模块,device link在模块加载时被添加,那么在卸载时删除。对device link添加的限制,在删除时也需要。而被驱动core管理的device link可以自动删除。

在添加device link时可考虑多个标志,前面已经提到两个:DL_FLGA_STATELES表示supplier和consumer之间没有驱动存在的依赖,标志DL_FLAG_PM_RUNTIME表示希望runtime PM集成。

两个另外的标志也是在consumer的probe回调中增加device link时使用:DL_FLAG_RPM_ACTIVE用于唤醒supplier,以防它在consumer suspend之前就suspend。DL_FLAG_AUTOREMOVE_CONSUMER可用于consumer probe失败或unbind延迟,device link可以自动释放。

类似的,当device link 被添加到supplier的probe回调函数里时,DL_FLAG_AUTOREMOVE_SUPPLIER会在supplier驱动卸载时自动释放device link。

注意,标志DL_FLAG_AUTOREMOVE_CONSUMER/DL_FLAG_AUTOREMOVE_SUPPLIER/DL_FLAG_AUTOPROBE_CONSUMER和DL_FLAG_STATELESS一起用时无效。

Limitations

驱动开发者需要知道管理的device link的存在依赖性可能导致consumer的probe延迟。当在某个initcall level到来时consumer可能被要求被调用,更糟糕的是,如果supplier的驱动在黑名单上或没有被调用时,consumer将永远不会被probe。

并且,管理的device link 不能直接删除。它们可以通过DL_FLAG_AUTOREMOVE_CONSUMER和DL_FLAG_AUTOREOVE_SUPPLIIER让驱动core来删除。但对于stateless的device link,一般通过调用device_link_add()来添加同时也会调用device_link_del()或device_link_remove()来删除。

在调用device_link_add()时附带标志DL_FLAG_ACTIVE和DL_FLAG_STATELSS会导致在device_link_del()和device_link_del()后supplier设备的引用计数不等于0。如果device_link_add()被调两次却没有删除device link,这会导致consumer仍处于runtime active状态,但supplier的runtime引用计数减少到进入suspended状态。
[为了修复这种情况,需要将consumer进入suspend,或在函数device_link_add()和device_link_del()之间调用pm_runtime_set_suspended()]

Implementation

设备层次结构是一棵树,一旦有device link添加时会变成无循环图。

设备的suspend/resume顺序是由dpm_list决定的。设备的shutdown顺序是由devices_kset决定的。若没有device link的存在,这两条list是平的,一维的设备树,一个设备在父设备后。设备树的形成是通过遍历ACPI或DTS树获取。

一旦device link被添加,lists需要满足设备添加到supplier的限制。为确保,一旦增加device link,consumer和整个子图都需要移到list的末尾。(可在device_link_add()时调用device_reorder_to_tail())

为了防止图中形成依赖loop,需要确保device link中supplier不依赖于consumer或任何子设备或consumer设备的consumer设备。

显而易见这要求不能从父设备到子设备之间增加device link。而从子设备到父设备是允许的。因为驱动core已经保证父子设备正确的suspend/resume/shutdown顺序,这种device link仅在驱动存在依赖时有意义。在这种情况下驱动需要注意。一个更合适的方法是简单的使probe延迟产生或添加设备flag让子设备在父设备后probe。

State Machine

DL_STATE_NONE:表示不需要关注驱动的存在性;

DL_STATE_DORMANT:表示supplier/consumer的驱动都不存在;

DL_STATE_AVAILABLE:表示supplier的驱动存在,但consumer的驱动不存在;

DL_STATE_CONSUMER_PROBE: 表示consumer在probe过程中(supplier驱动存在)

DL_STATE_ACTIVE: 表示supplier和consumer的驱动都存在;

DL_STATE_SUPPLIER_UNBIND:表示supplier的驱动在unbind过程中;

                .=============================.
                |                             |
                v                             |
DORMANT <=> AVAILABLE <=> CONSUMER_PROBE => ACTIVE
   ^                                          |
   |                                          |
   '============ SUPPLIER_UNBIND <============'
  • device link的初始状态自动由device_link_add()基于supplier和consumer的驱动是否存在决定。如果在device probe之前创建link,它的状态为DL_STATE_DORMANT。
  • 当supplier绑定到驱动时,link进入到状态DL_STATE_AVAILABLE。(通过driver_bound()调用device_links_driver_bound())
  • 在consumer设备probe前,通过检查consumer设备不在wait_for_supplier链和检查这些list是否DL_STATE_AVAILABLE状态,来验证supplier驱动的存在。link的状态更新到DL_STATE_CONSUMER_PROBE。(通过really_probe()调用device_links_check_suppliers())。这会防止supplier unbound。(调用device_links_unbind_consumers()调用wait_for_device_probe())
  • 如果probe失败,supplier的link状态返回到DL_STATE_AVAILABLE.(通过really_probe()调用device_links_no_driver())
  • 如果probe成功,supplier的link状态变成DL_STATE_ACTIVE。(通过driver_bound()调用device_links_driver_bound())
  • 当consumer的驱动移除时,supplier的link状态返回到DL_STATE_AVAILABLE。(通过device_links_driver_cleanup()调用__device_links_no_driver())
  • 在supplier驱动移除时,consumer的驱动也没有bound到驱动,link状态变为DL_STATE_SUPPLIER_UNBIND。(通过__device_release_driver()调用device_links_busy())这时不允许consumer进行绑定操作。(通过really_probe()调用device_links_check_suppliers())绑定的consumer从驱动释放。consumer的probe操作会一直等待直到完成。(通过__device_release_driver()调用device_links_unbind_consumers())一旦consumer所有的link都在DL_STATE_SUPPLIER_UNBIND,supplier的驱动会释放,并且link的状态返回到DL_STATE_DORMANT。(通过__device_release_driver()调用device_links_driver_cleanup())

API

struct device_link * device_link_add(struct device * consumer, struct device * supplier, u32 flags)

在consumer和supplier之间创建device link

void device_link_del(struct device_link * link)

删除两个设备间的stateless link.

void device_link_remove(void * consumer, struct device * supplier)

删除consumer和supplier的设备间的device link.

使用的例子

对于SCSI层,已经支持SCSI硬盘的runtime PM, 即在硬盘空闲时自动进入suspend状态,当业务存在是会唤醒硬盘。为了支持SAS控制器的runtime PM,可以在SAS控制器与所连接的硬盘间建立链接:当所有硬盘进入suspend时,SAS控制器可以进入suspend;当需要唤醒硬盘时,先唤醒SAS控制器。如果SAS控制器与硬盘之间为父子设备关系,自然会有这种关系,但若不是父子设备,此时可以使用device link来建立起这种关系。

graph LR
A[SCSI DEVICE]-->B[SAS controller]
  • 若设置DL_FLAG_STATELESS,需要驱动找地方调用device_link_del()或device_link_remove()删除link;
  • 若设置DL_AUTOREMOVE_SUPPLIER,系统在supplier unbound时释放link;
  • 若设置DL_AUTOREMOVE_CONSUMER,系统在consumer unbound时释放link;
  • 若都没有设置,系统在consumer/supplier设备unregister时释放link.
  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值