2019-04-03 17:05:09 baidu_25816669 阅读数 765
  • 韦东山升级版嵌入式视频之快速入门

    韦东山老师为啥要录升级版嵌入式视频? 200x年左右,嵌入式Linux在全世界、在中国刚刚兴起。 我记得我2005年进入中兴时,全部门的人正在努力学习Linux。 在2008年,我写了一本书《嵌入式Linux应用开发完全手册》。 它的大概内容是:裸机、U-boot、Linux内核、Linux设备驱动。 那时还没有这样讲解整个系统的书, 芯片厂家Linux开发包也还不完善,从bootloader到内核,再到设备驱动都不完善。 有全系统开发能力的人也很少。 于是这书也就恰逢其时,变成了畅销书。 我也根据这个思路录制了视频:裸机、U-boot、Linux内核、Linux设备驱动。 收获些许名声,带领很多人进入Linux世界。 11年过去了,嵌入式Linux世界发生了翻天覆地的变化 ① 基本系统能用 芯片厂家都会提供完整的U-boot、Linux内核、芯片上硬件资源的驱动。 方案厂家会做一些定制,比如加上某个WIFI模块,会添加这个WIFI模块的驱动。 你可以使用厂家的原始方案,或是使用/借鉴方案商的方案,做出一个“能用”的产品。 ② 基础驱动弱化;高级驱动专业化 基础的驱动,比如GPIO、UART、SPI、I2C、LCD、MMC等,有了太多的书籍、视频、示例代码,修修改改总是可以用的。 很多所谓的驱动工程师,实际上就是“调参工程师”。 我们群里有名的火哥,提出了一个概念:这些驱动就起一个“hardware enable”的作用。 高级的驱动,比如USB、PCIE、HDMI、MIPI、GPU、WIFI、蓝牙、摄像头、声卡。 体系非常复杂,很少有人能讲清楚,很多时候只是一笔带过。 配置一下应用层工具就了事,能用就成。 这些高级驱动,工作中需要专门的人来负责,非常专业。 他们是某一块的专家,比如摄像头专家、音频专家。 ③ 项目为王 你到一个公司,目的是把产品做出来,会涉及APP到内核到驱动全流程。 中小公司玩不起华为中兴的配置,需要的是全面手。 大公司里,只负责很小很小一块的镙丝钉,位置也不太稳固啊。 所以,如果你不是立志成为某方面的专家,那就做一个全栈工程师吧。 ④ 调试很重要 都说代码是3分写7分调,各种调试调优技术,可以为你的升职加薪加一把火。 基于上述4点,我录制的全新视频将有这些特点: 1. 快速入门, 2. 实战项目, 3. 驱动大全, 4. 专题, 5. 授人以渔, 6. 要做任务 另外,我们会使用多款芯片同时录制,先讲通用的原理,再单独讲各个板子的操作。 这些芯片涵盖主流芯片公司的主流芯片,让你学习工作无缝对接。 1.快速入门 入门讲究的是快速,入门之后再慢慢深入, 特别是对于急着找工作的学生,对于业余时间挑灯夜读的工作了的人,一定要快! 再从裸机、U-boot、内核、驱动这样的路线学习就不适合了,时间就拉得太长了。 搞不好学了后面忘了前面。 并且实际工作中并不需要你去弄懂U-boot,会用就行:U-boot比驱动还复杂。 讲哪些内容? 怎么讲呢? 混着讲 比如先讲LED APP,知道APP怎么调用驱动,再讲LED硬件原理和裸机,最后讲驱动的编写。 这样可以快速掌握嵌入式Linux的整套开发流程, 不必像以前那样光学习裸机就花上1、2个月。 而里面的裸机课程,也会让你在掌握硬件操作的同时,把单片机也学会了。 讲基础技能 中断、休眠-唤醒、异步通知、阻塞、内存映射等等机制,会配合驱动和APP来讲解。 这些技能是嵌入式Linux开发的基础。 而这些驱动,只会涉及LED、按制、LCD等几个驱动。 掌握了这些输入、输出的驱动和对应的APP后,你已经具备基本的开发能力了。 讲配置 我们从厂家、从方案公司基本上都可以拿到一套完整的开发环境,怎么去配置它? 需要懂shell和python等配置脚本。 效果效率优先 以前我都是现场写代码、现场写文档,字写得慢,降低了学习效率。 这次,效果与效率统一考虑,不再追求所有东西都现场写。 容易的地方可先写好代码文档,难的地方现场写。 2.实战项目 会讲解这样的涉及linux网关/服务器相关项目(不限于,请多提建议):                      定位为:快速掌握项目开发经验,丰满简历。 涉及的每一部分都会讲,比如如果涉及蓝牙,在这里只会讲怎么使用,让你能写出程序;如果要深入,可以看后面的蓝牙专题。 3. 驱动大全 包括基础驱动、高级驱动。 这些驱动都是独立成章,深入讲解。 虽然基础驱动弱化了,但是作为Linux系统开发人员,这是必备技能,并且从驱动去理解内核是一个好方法。 在讲解这些驱动时,会把驱动的运行环境,比如内核调度,进程线程等概念也讲出来,这样就可以搭建一个知识体系。 没有这些知识体系的话,对驱动的理解就太肤浅了,等于在Linux框架下写裸机,一叶障目,不见泰山。 定位为:工具、字典,用到再学习。 4. 专题 想深入学习的任何内容,都可独立为专题。 比如U-boot专题、内核内存管理专题、systemtap调试专题。

    2000 人正在学习 去看看 韦东山

某些特殊的应用场合可能要求PCIe设备能够以高可靠性持续不间断运行,为此,PCIe总线采用热插拔(Hot Plug)和热切换(Hot Swap)技术,来实现不关闭系统电源的情况下更换PCIe卡设备。

注:本文将简单地介绍一下PCIe总线的热插拔机制,关于热切换(Hot Swap),请参考PCIe Spec的相关章节。

 

PCIe总线的热插拔主要指的是PCIe卡设备的热插拔,以及相关的实现机制等。PCIe卡有两个用于热插拔机制的边带信号——PRSNT1#和PRSNT2#。PCIe卡设备上的这两个信号之间是短路的,PCIe插槽的PRSNT1#被固定地连接到地,PRSNT2#则被上拉。且这两个信号的金手指长度要比其他的信号的金手指长度要短一点。如下图所示,当PCIe卡设备未被完全插入插槽时,插槽的PRSNT2#信号由于上拉的作用,将一直处于高点平状态。当PCIe卡设备被完全插入插槽后,插槽上的PRSNT2#信号则会被PCIe卡设备的短路线连接到地,从而使得其变为低电平。换句话说,从插槽的角度看,当PRSNT2#位高电平时,则认为PCIe卡设备未能正确插入或者无PCIe卡设备;当PRSNT2#位低电平时,表明PCIe卡设备被正确地插入插槽中。

注:PCIe总线除了有一个Base Spec之外,还有一个关于PCIe卡设备的Spec——PCIe Card ElectroMechanical Spec(CEM)。

与PCI总线不同,PCIe总线采用的是点到点的连接(Point-to-Point Connections),因此其并不像PCI总线那样需要用于卡设备的隔离逻辑(Isolation Logic),但是每个端口(桥设备中的,如Root和Switch)都必须包含一个独立的热插拔控制器(Hot Plug Controller),如下图所示:

当然,热插拔不仅仅是硬件的事,其需要软硬件协同实现。要想实现热插拔功能,操作系统、主板热插拔驱动器、PCIe卡设备驱动以及PCIe卡硬件功能都必须支持热插拔,缺一不可。从PCIe卡设备硬件功能的角度来看,其需要支持Quiesce命令、Pause命令(可选)、Start命令和Resume命令。

桥设备(Switch等)中还需要支持热插拔控制器(Hot Plug Controller),如下图所示,这里就不详细介绍了。具体请参考PCIe Spec相关章节内容。

配置空间中,与热插拔相关的寄存器如下图所示:

2013-02-25 14:49:59 fcryuuhou 阅读数 2372
  • 韦东山升级版嵌入式视频之快速入门

    韦东山老师为啥要录升级版嵌入式视频? 200x年左右,嵌入式Linux在全世界、在中国刚刚兴起。 我记得我2005年进入中兴时,全部门的人正在努力学习Linux。 在2008年,我写了一本书《嵌入式Linux应用开发完全手册》。 它的大概内容是:裸机、U-boot、Linux内核、Linux设备驱动。 那时还没有这样讲解整个系统的书, 芯片厂家Linux开发包也还不完善,从bootloader到内核,再到设备驱动都不完善。 有全系统开发能力的人也很少。 于是这书也就恰逢其时,变成了畅销书。 我也根据这个思路录制了视频:裸机、U-boot、Linux内核、Linux设备驱动。 收获些许名声,带领很多人进入Linux世界。 11年过去了,嵌入式Linux世界发生了翻天覆地的变化 ① 基本系统能用 芯片厂家都会提供完整的U-boot、Linux内核、芯片上硬件资源的驱动。 方案厂家会做一些定制,比如加上某个WIFI模块,会添加这个WIFI模块的驱动。 你可以使用厂家的原始方案,或是使用/借鉴方案商的方案,做出一个“能用”的产品。 ② 基础驱动弱化;高级驱动专业化 基础的驱动,比如GPIO、UART、SPI、I2C、LCD、MMC等,有了太多的书籍、视频、示例代码,修修改改总是可以用的。 很多所谓的驱动工程师,实际上就是“调参工程师”。 我们群里有名的火哥,提出了一个概念:这些驱动就起一个“hardware enable”的作用。 高级的驱动,比如USB、PCIE、HDMI、MIPI、GPU、WIFI、蓝牙、摄像头、声卡。 体系非常复杂,很少有人能讲清楚,很多时候只是一笔带过。 配置一下应用层工具就了事,能用就成。 这些高级驱动,工作中需要专门的人来负责,非常专业。 他们是某一块的专家,比如摄像头专家、音频专家。 ③ 项目为王 你到一个公司,目的是把产品做出来,会涉及APP到内核到驱动全流程。 中小公司玩不起华为中兴的配置,需要的是全面手。 大公司里,只负责很小很小一块的镙丝钉,位置也不太稳固啊。 所以,如果你不是立志成为某方面的专家,那就做一个全栈工程师吧。 ④ 调试很重要 都说代码是3分写7分调,各种调试调优技术,可以为你的升职加薪加一把火。 基于上述4点,我录制的全新视频将有这些特点: 1. 快速入门, 2. 实战项目, 3. 驱动大全, 4. 专题, 5. 授人以渔, 6. 要做任务 另外,我们会使用多款芯片同时录制,先讲通用的原理,再单独讲各个板子的操作。 这些芯片涵盖主流芯片公司的主流芯片,让你学习工作无缝对接。 1.快速入门 入门讲究的是快速,入门之后再慢慢深入, 特别是对于急着找工作的学生,对于业余时间挑灯夜读的工作了的人,一定要快! 再从裸机、U-boot、内核、驱动这样的路线学习就不适合了,时间就拉得太长了。 搞不好学了后面忘了前面。 并且实际工作中并不需要你去弄懂U-boot,会用就行:U-boot比驱动还复杂。 讲哪些内容? 怎么讲呢? 混着讲 比如先讲LED APP,知道APP怎么调用驱动,再讲LED硬件原理和裸机,最后讲驱动的编写。 这样可以快速掌握嵌入式Linux的整套开发流程, 不必像以前那样光学习裸机就花上1、2个月。 而里面的裸机课程,也会让你在掌握硬件操作的同时,把单片机也学会了。 讲基础技能 中断、休眠-唤醒、异步通知、阻塞、内存映射等等机制,会配合驱动和APP来讲解。 这些技能是嵌入式Linux开发的基础。 而这些驱动,只会涉及LED、按制、LCD等几个驱动。 掌握了这些输入、输出的驱动和对应的APP后,你已经具备基本的开发能力了。 讲配置 我们从厂家、从方案公司基本上都可以拿到一套完整的开发环境,怎么去配置它? 需要懂shell和python等配置脚本。 效果效率优先 以前我都是现场写代码、现场写文档,字写得慢,降低了学习效率。 这次,效果与效率统一考虑,不再追求所有东西都现场写。 容易的地方可先写好代码文档,难的地方现场写。 2.实战项目 会讲解这样的涉及linux网关/服务器相关项目(不限于,请多提建议):                      定位为:快速掌握项目开发经验,丰满简历。 涉及的每一部分都会讲,比如如果涉及蓝牙,在这里只会讲怎么使用,让你能写出程序;如果要深入,可以看后面的蓝牙专题。 3. 驱动大全 包括基础驱动、高级驱动。 这些驱动都是独立成章,深入讲解。 虽然基础驱动弱化了,但是作为Linux系统开发人员,这是必备技能,并且从驱动去理解内核是一个好方法。 在讲解这些驱动时,会把驱动的运行环境,比如内核调度,进程线程等概念也讲出来,这样就可以搭建一个知识体系。 没有这些知识体系的话,对驱动的理解就太肤浅了,等于在Linux框架下写裸机,一叶障目,不见泰山。 定位为:工具、字典,用到再学习。 4. 专题 想深入学习的任何内容,都可独立为专题。 比如U-boot专题、内核内存管理专题、systemtap调试专题。

    2000 人正在学习 去看看 韦东山

    PCI热插拔技术,可以有效避免由更换外设引起的服务器系统停机,对于提高服务器系统可用性和可扩展性意义重大。本文讨论了PCI Express热插拔所涉及的软件因素,并基于此,剖析了Linux 2.6.10内核下PCI Express 插槽热插拔子系统的关键实现机制。

    一 相关技术与研究

    1997年,PCI SIG制定了第一个PCI热插拔规范,其中定义了支持热插拔所必需的平台、板卡和软件元素。PCI SIG推出了标准热插拔控制器规范(SHPC SPEC),其中明确了热插拔的标准使用模式和严格的寄存器组要求,并且允许操作系统提供商在平台特定的软件之外提供热插拔支持,逐步完成了热插拔标准制定工作,进入技术的全面推广阶段。 2002年以后,Intel把热插拔作为一种天然属性赋予新推出的PCI Express规范,PCI Express热插拔总结了五年来工业标准的经验,具有如下特点:

    

        

  • 基于SHPC模式并对其进行了功能扩展;

        

  • PCI Express从设计上就把热插拔寄存器集成进入了其标准的性能寄存器组(而在SHPC 1.0中,热插拔寄存器是附加的);

        

  • 提供给操作系统一个统一的热插拔硬件寄存器接口,赋予了操作系统进行原生热插拔(绕过了传统基于BIOS的热插拔方法)的能力;

        

  • 通过在基本的体系机构层次定义热插拔必须的硬件要求,完善了一种标准的使用模式;

        

  • 提供了规格参数来保证OEM产品的低成本而高平台可靠性。

    具有完整功能的PCI Express热插拔系统在平台硬件和固件支持之外,还必须有操作系统以及设备驱动程序的支持。

    在各大芯片厂商纷纷推出支持PCI热插拔的产品的同时,Microsoft,Novell,SCO等公司在他们相关的操作系统中也都包括了支持热插拔的技术, Novell NetWare 4.11&5和SCO UnixWare 7以及更新版本支持完整PCI热插拔(热替换和热添加)。Microsoft Windows NT 4.0利用Compaq 服务器支持盘(SSD) for NT 4.0,提供了PCI热替换支持,进入2000年后,Microsoft Windows 2000也内建了对PCI热插拔技术的全面软件支持。对于GNU/Linux,从2001年1月份的内核2.4版本开始,PCI 热插拔开始成为其标准特性之一,到2.6 版本,热插拔功能已经被整合进入核心设备模型。现在,几乎所有的Linux发行版本,包括RedHat, Debian和 United Linux,都对PCI 热插拔提供了良好支持,并分别有所扩展。BSD分支在这方面起步较晚,OpenBSD在2004年三月份的3.6版本中才出现了不含设备驱动的设备热插拔参考框架,而FreeBSD 到5.3版本为止,尚未提供对PCI 热插拔的支持。

    作为2002年出现的规范,各个通用操作系统对PCI Express的支持才刚刚起步。在微软公司的 Windows 2000, Windows XP, and Windows Server 2003中,PCI Express设备被当作PCI设备进行热插拔,其他PCI Express的高级功能可以由固件激活。在Windows Longhorn中,才提供对PCI Express高级特性的原生支持。GNU/Linux在内核版本2.6.9中开始专门提供对于PCI Express 热插拔的支持,中间历经软件架构的改动,逐渐发展为以内核版本2.6.10以及内核版本2.6.12下两种不同的PCI Express热插拔驱动架构。本文主要讨论2.6.10所采用的PCI Express热插拔驱动架构,并在最后给出2.6.12的新特性。

    

    

    

    

    回页首

    三 PCI Express 热插拔的软件支持

    根据规范,一个完整的 Native PCI Express 热插拔系统需要几方面的相互配合,分别为硬件元素、固件元素和软件元素。硬件元素是指主板总线系统的电气特性方面的支持,包括热插拔控制器(Hot-Plug Controller)、卡槽电源切换逻辑(Card Slot Power Switching Logic)、板卡重置逻辑(Card Reset Logic)、电源指示灯(Power Indicator)、提示按钮(Attention Button)和板卡存在检测引脚(Card Present Detect Pins)等等;固件元素是指主板BIOS必须对热插拔提供的支持,要实现Native PCI Express热插拔,固件必须提供OSHP方法或ACPI _OSC方法之一;软件元素是指操作系统操综合使用PCI Express 热插拔所必须提供的功能组件。

    软件元素

    为了操纵平台的硬件元素,提供PCI Express热插拔服务,我们必须实现表1所示的主要热插拔软件元素。

    

    

    PCI Express服务模型

    PCIE热插拔(PCIEHP)子系统中的标准热插拔系统驱动程序通过检查PCI Express性能数据结构中的插槽性能寄存器(Slot Capabilities register),来获取硬件的热插拔部件信息。这些寄存器所能反映的信息如下:

    

  • 插槽是否支持热插拔

        

  • 设备是否支持不通知软件的突然拔出操作

        

  • 提示灯、控制器等硬件元素是否存在

        一旦软件完成了PCI Express设备热插拔功能的开启和配置工作,热插拔行为(例如移出或插入请求,电源故障)就可以向系统热插拔处理机制提交系统中断和电源管理事件。这种热插拔处理机制由操作系统独立提供,图1展示了PCI Express Native热插拔的服务模型,并展示了它与传统的基于ACPI的模型之间的区别。

        图1 PCI Express热插拔服务模型比较

        

        

        

        

        

        

        回页首

        四Linux 2.6.10 PCIE 热插拔子系统代码分析

        热插拔框架

        为了提供PCI Express热插拔服务,Linux 2.6.10 PCIE热插拔(PCIEHP)子系统必须实现用户操作界面、热插拔服务程序和标准热插拔系统驱动等热插拔软件元素,并使软件的行为符合热插拔标准使用模式[4]。另外,一些特定的热插拔功能必须与支持热插拔的设备驱动结合在一起发挥作用。

        PCIEHP核内部分的主体是一个核心线程,其控制逻辑以模块的方式加载入内核,负责监控PCI Express总线上的热插拔事件,并做相关处理;核外部分是一个用户空间的脚本,从内核中调用,并根据内核传回的信息执行后续处理过程。

        1 热插拔驱动程序生命周期

        从PCIEHP子系统启动,到子系统被卸载期间,PCIEHP完全接管系统中PCI Express插槽热插拔事件。

        PCIEHP子系统启动时,首先要开启用户态守护进程。然后初始化通知机制,开启PCI Express热插拔事件处理核心线程,为操作系统中各总线数据结构分别预初始化一个热插拔插槽列表。接着,根据内核编译时是否指定ACPI式资源管理,初始化设备资源管理方式。最后,根据设备标识,在系统中注册PCI Express热插拔驱动程序,并将其绑定到总线上所有可能挂载热插拔插槽的PCIE桥接设备,然后初始化对应的热插拔控制器,分配资源,开启定时的中断轮询机制。

        卸载PCIEHP子系统,首先释放热插拔控制器全局列表中每个控制器所占用的资源,释放热插拔插槽全局列表中每个插槽所占用的资源,终止通知机制的运行,结束PCI Express热插拔事件处理核心线程;接着,根据内核编译时是否指定ACPI式资源管理,释放相关设备占用的系统资源;然后,注销PCI Express热插拔驱动程序;在处理完当前热插拔事件后,终止核外守护进程。

        在PCIEHP加载后,为了在热插拔执行过程中追踪其状态变化,我们把热插拔插槽的状态抽象如下:

        

          

    • 静态 (STATIC STATE ),

          正常工作或者空闲的时段,

          

    • 开启前闪烁提示态(BLINKINGON STATE)

          发出开启请求到确认之前的时段

          

    • 关闭前闪烁提示态 (BLINKINGOFF STATE)

          发出关闭请求到确认之前的时段

          

    • 开启态 (POWERON STATE)

          执行开启操作的时段

          

    • 关闭态(POWEROFF STATE)

          执行关闭操作的时段

        各个插槽状态之间的转换关系如图2所示:

        图2 插槽状态转换图

        

        

        2 用户控制脚本/sbin/hotplug和核内外通信机制

        在内核热插拔处理完毕后,它会在核内调用一个用户态脚本hotplug,这个脚本将根据内核提供的信息进行后续处理工作。它是设备热插拔通告的用户空间部分,它接收内核传出的热插拔操作类型和环境变量,处理设备挂载和卸载操作。通常,hotplug会根据设备类型调用一个策略脚本,针对设备和系统当前参数进行后续的配置工作。例如,对于网卡,可能会调用脚本指定IP地址,网关和域名服务器等等,对于存储设备,可能会调用mount命令来加载它到文件系统内。

        内核传送给hotplug的信息包含热插拔操作类型(例如PCI),并且在一个环境变量中提供本次操作相关信息:

        

          

    • 行为种类:添加或删除

          

    • 对象设备所属PCI 类、子类和编程接口

          

    • 对象制造商标志和设备标志

          

    • 对象总线地址、插槽地址和功能函数编号

        核外的后续配置工作涉及如下文件:

        /etc/hotplug/pci.rc

        /ect/hotplug/pci.Agent

        /etc/rc.d/init.d/hotplug

        在内核中,由kernel/kmod.c中的函数

        int call_usermodehelper (char *path, char **argv, char **envp, int wait)来开启用户态脚本/sbin/hotplug。在参数表中,path表示了所启动的核外应用程序的路径,argv是应用程序的参数表,envp是环境变量列表,wait则指出了是否同步等待应用程序执行完毕再返回执行结果的状态。

        3 PCIE 热插拔模块构成

        为了使用PCI Express Native Hotplug,我们必须在编译的时候开启对应的功能模块。

        在内核配置中,PCIE Hotplug对应的开关为HOTPLUG_PCI_PCIE,它依赖于HOTPLUG_PCI。如果你的主板支持PCI Express Native Hotplug,可以选择Y;如果你只是想把这个驱动作为模块来编译,那么选择M,此模块叫做pciehp,在源代码\dirver\pci\hotplug\Kconfig文件中,你可以看到:

        config HOTPLUG_PCI_PCIE

         tristate "PCI Express Hotplug driver"

         depends on HOTPLUG_PCI

        完整的pciehp模块功能涉及到如下几个文件pci_hotplug_core.c, pciehp_core.c,pciehp_ctrl.c,pciehp_pci.c pciehp_hpc.c,另外根据是否开启了ACPI,包含pciehprm_acpi.c 或者pciehprm_nonacpi.c,在源代码\dirver\pci\hotplug\Makefile文件中,你可以看到:

        pci_hotplug-objs := pci_hotplug_core.o

        pciehp-objs := pciehp_core.o \

         pciehp_ctrl.o \

         pciehp_pci.o \

         pciehp_hpc.o

        ifdef CONFIG_ACPI_BUS

         pciehp-objs += pciehprm_acpi.o

        else

         pciehp-objs += pciehprm_nonacpi.o

        endif

        代码的主要任务就是在所有支持热插拔的PCIE桥上加载热插拔驱动程序,监控热插拔事件,并根据类型,如是热插入事件、热拔出事件还是电源故障等分别予以处理。

        热插拔驱动的加载

        热插拔驱动程序的加载所进行的主要工作是开启并初始化PCIE热插拔的内核线程,这部分代码位于/driver/pci/hotplug/pciehp_core.c中。入口函数为:

        static int __init pcied_init(void)

        #ifdef CONFIG_HOTPLUG_PCI_PCIE_POLL_EVENT_MODE

         pciehp_poll_mode = 1;

        #endif

         retval = pcie_start_thread();

         if (retval)

         goto error_hpc_init;

         retval = pciehprm_init(PCI);

         if (!retval) {

         retval = pci_register_driver(&pcie_driver);

         dbg("pci_register_driver = %d\n", retval);

         info(DRIVER_DESC " version: " DRIVER_VERSION "\n");

         }

        

        pcied_init中所涉及的关键函数分析如下:

        retval = pcie_start_thread();

        初始化并开启通知机制:

        pciehp_event_start_thread()启动事件监控处理线程

        然后初始化slot列表,系统中每个bus给一个slot列表。

        struct pci_func *pciehp_slot_list[256]; 都设为NULL

        retval = pciehprm_init(PCI);

        原型:int pciehprm_init(enum php_ctlr_type ctlr_type)

        初始化资源(区别两种情况:acpi和非acpi的)在非acpi的初始化方式下,调用空函数legacy_pciehprm_init_pci();在pcihprm_nonacpi.h中,定义了irq_info,irq_routing_table两个结构。在acpi初始化方式下,通过pciehprm_acpi_scan_pci()在acpi树下遍历搜寻PCI设备。不论acpi和非acpi的,都要求php_ctlr_type为PCI。

        retval = pci_register_driver(&pcie_driver);

        注册并初始化PCI桥热插拔驱动程序模块。

        把设备驱动加入已注册设备驱动列表,即使在期间没有相应设备出现驱动程序仍然保持有效。

         int count = 0;

         /* initialize common driver fields */

        

        用来泛化之,可以把drv->driver看作为drv的基类信息

         drv->driver.name = drv->name;

         drv->driver.bus = &pci_bus_type;

         drv->driver.probe = pci_device_probe;

         drv->driver.remove = pci_device_remove;

         drv->driver.kobj.ktype = &pci_driver_kobj_type;

         pci_init_dynids(&drv->dynids);

         count = driver_register(&drv->driver);

        

        注意:这里是如何从基类drv->driver一般设备的驱动向子类drv这个pci设备的驱动逆向关联的--使用CONTAINER宏

        pcie_driver为PCIE驱动程序对象,定义在 pciehp_core.c中

         static struct pci_driver pcie_driver = {

         //驱动名称定义为"pciehp"

        .name= PCIE_MODULE_NAME,

        // id_table指定探测函数probe所应用的范围,这里在表中指定为所有的PCI桥设备

         .id_table = pcied_pci_tbl,

        //probe为指定的设备探测函数

         .probe = pcie_probe,

        };

        static struct pci_device_id pcied_pci_tbl[] = {

         {

         //此处选择所有PCI桥

        .class = ((PCI_CLASS_BRIDGE_PCI

        设备探测pcie_probe:

        static int pcie_probe

        (struct pci_dev *pdev, const struct pci_device_id *ent)

        in pcie-core.c

        已经确定了pdev就是pcie_driver 所匹配的PCI桥设备,而且它在pcie_driver中所对应的设备特征号是*ent,就可以对其进行进一步的初始化和探测。

        具体行为如下:

        

          

    • 绑定热插拔插槽

          

    • 设置了控制器及其状态

          

    • 注册中断处理函数

          

    • 读写配置头,然后分配资源,最后对插槽检测,挂接

        热插拔事件的监控处理线程

        热插拔驱动程序对于热插拔事件的轮询和通知采用异步机制,对热插拔功能部件的操纵是通过读写相关寄存器组进行的。主要功能函数关系请参见图3:

        图3 插槽热插拔事件处理函数关系

        

        

        图3中主要函数的功能介绍如下:

        A插槽事件监控线程

        作为热插拔活动最直接的信息,插槽事件由硬件操作并置位相关寄存器组,系统软件可以通过定时轮询或者中断方式获取事件信息,执行对应的事件预处理函数。插槽事件如下:

        

          

    • 热插拔命令到达

          

    • 插槽锁状态改变

          

    • 适配卡存在状态改变

          

    • 电源出现故障

        php_ctlr->int_poll_timer.function = &int_poll_timeout 其中php_ctlr是热插拔控制器状态php_ctlr_state_s类型,它定义于pciehp_hpc.h中,记录当前热插拔控制器重要状态,被用作HPC(controller)的控制器句柄;热插拔控制器controller位于文件pciehp.h中,描述了PCIE热插拔控制器的特征;

        定时轮询函数原型如下:

        static void int_poll_timeout(unsigned long lphp_ctlr)

        其中调用pcie_isr( 0, (void *)php_ctlr, NULL );

        当最后一个参数是NULL时,采用polling机制。

        static irqreturn_t pcie_isr(int IRQ, void *dev_id, struct pt_regs *regs);

        轮询机制通过定期(2second,使用一个计时器)读取ctrller对应的状态寄存器,来获取事件,然后调用ctrl状态参量对应的事件处理函数。也就是按钮、电源、MRL等等事件处理函数,并分别调用event_semaphore来激活event_thread.

        B事件预处理函数

        在插槽事件监控线程截获插槽事件后,它根据事件类型调用这组处理函数,执行完毕后,填写对应控制器上所挂接的事件队列,并激活睡眠等待的处理线程。可以激活睡眠中的处理线程的函数包括如下几个:

        pciehp_handle_attention_button :处理按钮事件

        pciehp_handle_switch_change:处理开关状态改变事件

        pciehp_handle_presence_change:处理存在性状态变化事件

        pciehp_handle_power_fault:处理电源故障事件

        pushbutton_helper_thread:按钮动作处理线程

        C热插拔事件处理核心线程event_thread

        event_thread的处理过程如下:

        在一个无限循环中,阻塞等待插槽事件发生

        当线程被某一事件唤醒后,

        如果 热插拔请求已通过延时确认

        进入热插拔请求处理函数

        否则

        轮询热插拔控制器队列:

        把控制器作为参数传给插槽事件处理函数

        其中

        if (pushbutton_pending)

         pciehp_pushbutton_thread(pushbutton_pending);

         else if (surprise_rm_pending)

         pciehp_surprise_rm_thread(surprise_rm_pending);

        前一个处理按钮事件,后一个处理突然拔出事件。

        参数pushbutton_pending是由如下函数提供的:

        static void pushbutton_helper_thread(unsigned long data)

        {

         pushbutton_pending = data;

         up(&event_semaphore);

        而这个函数又被作为定时task事件的行动部分,赋值给

        static void interrupt_event_handler(struct controller *ctrl)中的

        p_slot->task_event.function:

        函数:

        p_slot->task_event.function

        = (void (*)(unsigned long)) pushbutton_helper_thread;

        参数:

        p_slot->task_event.data

        = (unsigned long) p_slot;

        D插槽事件处理函数interrupt_event_handler

        根据控制器信息,获取控制器对应插槽事件

        尚有事件等待处理,则执行以下内容:

        逐一查看控制器所挂接事件队列的每个成员(根据事件类型):

        1. 请求取消

        取消前五秒内触发的一次热插拔请求

        2. 请求触发

        触发一次热插拔操作:导致一个五秒钟的延时,如果五秒内请求没有被拒绝,则确认前一个热插拔请求

        3. 电源故障

        弹出提示信息

        4. 其他

        读取插槽的功能寄存器,更新状态信息。

        E热插拔请求处理函数pciehp_pushbutton_thread

        一个定期运行的程序,处理当前插槽上阻塞的请求,根据请求类型:对插槽中的设备进行热移出(F)或者热添加操作(G)。

        F插槽热拔出设备pciehp_disable_slot

        保证所除去的不是视频控制器

        卸载所除去的适配卡占用的系统资源,更新总线结构,关闭电源

        通知用户态守护进程

        更新插槽状态

        G插槽热添加设备pciehp_enable_slot

        1. 执行适配卡添加的一系列相关操作: 失败恢复预处理,存在性检验、打开电源,检查link training状态,获取设备基本信息,配置设备,为设备建立相关数据结构,挂接到上级总线等。

        2. 为新添加的设备查找并挂接驱动程序。

        3. 特别地,对于桥接设备,把它挂接到上级总线后,还要继续对其下级总线进行扫描和挂接。

        4. 通知用户态守护进程。

        5. 更新插槽状态。

        

        

        

        

        回页首

        五 PCIE板卡热插拔的标准过程

        热插拔PCI板卡可以使用提示按钮或用户界面来进行,下面我们介绍使用用户界面来启动热插入和热拔出的操作过程,以及Fedora C4T2下所采用的方式。

        设备的热插入

        1. 操作员安装卡,闭合插槽保护锁,保护锁感应器通知热插拔控制器把连接信号接通到插槽。

        2. 然后,操作员通知热插拔服务程序:卡已经被安装并可以激活。软件提示用户对此进行确认。

        3. 在操作员请求连接后,热插拔服务程序向控制着热插拔控制器的热插拔系统驱动程序下达命令,闪烁插槽的电源指示灯,提示操作员此时不可以拔动适配卡。

        4. 在热插拔软件对此请求进行确认期间内,电源指示灯继续闪烁。注意此时软件可能会拒绝这个安装请求(例如,安全策略此刻禁止插槽被激活)。另外,如果请求没有生效,软件拒绝请求并对热插拔控制器发出命令关闭电源指示灯。规范建议软件通过一条消息通知操作员请求被拒绝的原因。

        5. 如果请求被确认,热插拔服务程序对热插拔系统驱动发出请求,为插槽加电。

        6. 加电后,软件发出命令完全打开电源指示灯。

        7. 当link training完成后,操作系统指示平台配置程序赋予适配卡必需的资源,来配置适配卡的功能。

        8. 操作系统为PCI Express设备中的功能寻找恰当的驱动程序,并加载之。

        9. 接着系统调用驱动程序的初始化入口,并执行驱动的初始化代码。这些代码完成设备的设置,并填写设备的PCI 配置命令寄存器的相关标志位来激活设备。

        热移出设备

        1. 操作员通过指定适配卡所在物理插槽号码来初始化移出请求。

        2. 软件弹出窗口要求操作员确认请求。注意,此时电源指示灯保持开启状态。

        3. 操作员确认请求后,热插拔服务程序向热插拔系统驱动发出请求,要求热插拔控制器闪烁电源指示灯。注意此时软件可能会拒绝这个移出请求(例如,适配卡目前正被关键系统功能所使用)。另外,如果请求没有被确认,软件将拒绝请求并对热插拔控制器发出命令,重新开启电源指示灯。规范建议软件通过一条消息通知操作员请求被拒绝的原因。

        4. 如果请求被确认,热插拔服务程序将命令适配卡的设备驱动保持静默,也就是说驱动一方面必须停止向适配卡发出请求,另一方面必须完成或者终止所有已经发出的请求,并禁止适配卡产生新的事务(包括中断)。

        5. 软件发出命令,通过在插槽所连接的根端口或交换端口中的链接控制寄存器禁掉适配卡的链接。这使得链接两侧的端口均被禁止。

        6. 软件指示热插拔控制器禁掉插槽。

        7. 成功切断电源后,软件发出关闭电源指示灯命令。指示灯熄灭后,操作员可以开始安全地从插槽移出适配卡。打开插槽安全锁,热插拔控制器从插槽上撤除所有的信号(例如SMBus 和Vaux),此时卡可以被移出。

        8. 操作系统释放内存空间,I/O空间,中断线等曾经属于该设备的系统资源。

        Fedora下所采用热插拔实现的方式

        如果在最近一次编译中选择了PCI Express 热插拔功能,而且驱动是以模块方式存在,那么,可以在命令行下键入以下内容:

        modprobe pciehp

        如果驱动成功,则可以在/sys/bus/pci/slots/下面发现以可热插拔插槽编号命名的目录,进入相关目录,可以进行下一步操作。

        echo 1 >power开启某个插槽上的电源 ,进行热插入

        echo 0 >power关闭某个插槽上的电源,执行热拔出

        若不能加载pciehp驱动,一般是由于硬件不支持或者固件缺少OSHP方法或ACPI _OSC方法之一。

        

        

        

        

        回页首

        六 linux2.6.12中PCIE驱动模型的变化

        在linux2.6.10中,Linux驱动程序模型要求物理设备被单独的驱动程序独占访问。 PCI Express端口是一个拥有许多独立功能的PCI-PCI桥设备,作为一个简洁的方案,每个功能要分别实现其自己的驱动程序,但是这样造成了多个驱动程序在唯一的PCI-PCI桥设备中出现竞争的状况。也就是说,虽然PCI Express提供了如Power Management (PME)、 Advanced Error Reporting (AER)、 Hot-Plug (HP) 和Virtual Channel (VC) access等多种功能,但是如果某个PCI Express端口的native hotplug 驱动程序加载后,它就会独占这个PCI-PCI桥的端口,内核就不能再于其上加载其他功能的驱动程序了。为解决这个问题,在Linux内核版本2.6.12中,PCI Express的热插拔又有所改变,提出了PCI Express 端口总线驱动程序(PCI Express Port Bus Driver)的概念。

        在实现上,PCI Express 端口总线驱动程序管理主板上的所有PCI Express 端口,并且把所有提供的服务请求发送到对应的服务驱动程序上。其优点概括如下:

        

          

    • 允许多个服务驱动程序在一个PCI-PCI桥端口设备上同步运行。

          

    • 允许各个服务驱动程序相互不受干扰地独立实现。

          

    • 允许一个服务驱动程序在多个PCI-PCI桥端口设备上运行。

          

    • 管理PCI-PCI桥端口设备资源并分发它们到发出请求的服务驱动程序。

        例如,在注册热插拔驱动程序时,不再使用pci_register_driver直接向系统注册,而是使用int pcie_port_service_register(struct pcie_port_service_driver *new) 向端口总线驱动程序注册;在注销热插拔驱动程序时,不再使用pci_unregister_driver直接向系统注销,而是使用void pcie_port_service_unregister(struct pcie_port_service_driver *new)来向端口总线驱动程序注销;而端口总线驱动程序是直接注册注销到系统的。

        目前,PCI Express 端口总线驱动程序模型还在发展演变中。

        

        

        

        

        回页首

        七 评估与总结

        根据可用性理论,系统的可用度可以使用下列公式来计算:

        

        

        在高负荷运转的服务器上,商用可靠性的元件的故障更换并非小概率事件,对于更换故障元件和升级配件这样的事件,没有热插拔支持的系统必须停机断电进行处理,而具备热插拔支持的系统则仅仅需要很少的软件切换和拔插时间开销。从上面的公式我们可以看出,MTTR值越小,系统的可用性就越高。我们定义了一个比例因子V,V= MTTRnoHP/MTTRHP,根据业界经验,V的取值一般在10-100之间。描述可用性的一种常用的方法是使用"9"。如三个 9 表示 99.9% 的可用性,它表示一年大约有 8.5 小时的服务中断期。四个 9 (99.99%) 是更高一级的可用性,表示一年大约有 1 小时的服务中断期。根据可用度公式计算,在单个元件可靠度不变的情况下,Linux操作系统对PCI Express热插拔的支持,可以使服务器系统的外设相关可用性跃升一个等级(1个9),同时,PCI Express热插拔技术使得在线更换和升级PCI Express外设板卡成为可能,这使系统获得了良好的可扩展性。

        综上所述,本文讨论了PCI Express热插拔所涉及的软件因素,分析了linux2.6.10的PCI Express插槽热插拔功能PCIEHP子系统,并对热插拔支持在提高服务器系统外设相关可用性的作用进行了定量的分析。为了继续提高操作系统可用性和可扩展性支持能力,Linux PCI Express hotplug以下方面还有待发展:继续完善热插拔架构的开放性,以提供完整统一的接口供驱动开发人员编写其他设备的热插拔支持模块;在插槽热插拔之外,提供对Server IO Module(SIOM)热插拔的支持;完善热替换和热升级技术。这些问题都是非常具有挑战性的。

        参考资料

        

  • PCI Express Base Specification Revision 1.0

        

  • Linux Device Driver III edition O'Reilly出版社 2005

        

  • The Compaq PCI Hotplug Driver for Linux

        

  • PCI Express System Architecture Addison-Wesley 出版社 Sep 04, 2003

        

  • The PCI Express Port Bus Driver Guide HOWTO Tom L Nguyen

        [email=tom.l.nguyen@intel.com]tom.l.nguyen@intel.com[/email]

        

        

  • Introduction to PCI Express A Hardware and Software Developer's Guide Adam H. Wilen Justin

        

  • P. Schade Ron Thornburg

        

  •     www.sourceforge.org

        

        

  •     www.openbsd.org

        

        

  •     www.kernel.org

        

        

        本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/22878/showart_405348.html

  • 2017-02-20 00:27:51 liwg06 阅读数 522
    • 韦东山升级版嵌入式视频之快速入门

      韦东山老师为啥要录升级版嵌入式视频? 200x年左右,嵌入式Linux在全世界、在中国刚刚兴起。 我记得我2005年进入中兴时,全部门的人正在努力学习Linux。 在2008年,我写了一本书《嵌入式Linux应用开发完全手册》。 它的大概内容是:裸机、U-boot、Linux内核、Linux设备驱动。 那时还没有这样讲解整个系统的书, 芯片厂家Linux开发包也还不完善,从bootloader到内核,再到设备驱动都不完善。 有全系统开发能力的人也很少。 于是这书也就恰逢其时,变成了畅销书。 我也根据这个思路录制了视频:裸机、U-boot、Linux内核、Linux设备驱动。 收获些许名声,带领很多人进入Linux世界。 11年过去了,嵌入式Linux世界发生了翻天覆地的变化 ① 基本系统能用 芯片厂家都会提供完整的U-boot、Linux内核、芯片上硬件资源的驱动。 方案厂家会做一些定制,比如加上某个WIFI模块,会添加这个WIFI模块的驱动。 你可以使用厂家的原始方案,或是使用/借鉴方案商的方案,做出一个“能用”的产品。 ② 基础驱动弱化;高级驱动专业化 基础的驱动,比如GPIO、UART、SPI、I2C、LCD、MMC等,有了太多的书籍、视频、示例代码,修修改改总是可以用的。 很多所谓的驱动工程师,实际上就是“调参工程师”。 我们群里有名的火哥,提出了一个概念:这些驱动就起一个“hardware enable”的作用。 高级的驱动,比如USB、PCIE、HDMI、MIPI、GPU、WIFI、蓝牙、摄像头、声卡。 体系非常复杂,很少有人能讲清楚,很多时候只是一笔带过。 配置一下应用层工具就了事,能用就成。 这些高级驱动,工作中需要专门的人来负责,非常专业。 他们是某一块的专家,比如摄像头专家、音频专家。 ③ 项目为王 你到一个公司,目的是把产品做出来,会涉及APP到内核到驱动全流程。 中小公司玩不起华为中兴的配置,需要的是全面手。 大公司里,只负责很小很小一块的镙丝钉,位置也不太稳固啊。 所以,如果你不是立志成为某方面的专家,那就做一个全栈工程师吧。 ④ 调试很重要 都说代码是3分写7分调,各种调试调优技术,可以为你的升职加薪加一把火。 基于上述4点,我录制的全新视频将有这些特点: 1. 快速入门, 2. 实战项目, 3. 驱动大全, 4. 专题, 5. 授人以渔, 6. 要做任务 另外,我们会使用多款芯片同时录制,先讲通用的原理,再单独讲各个板子的操作。 这些芯片涵盖主流芯片公司的主流芯片,让你学习工作无缝对接。 1.快速入门 入门讲究的是快速,入门之后再慢慢深入, 特别是对于急着找工作的学生,对于业余时间挑灯夜读的工作了的人,一定要快! 再从裸机、U-boot、内核、驱动这样的路线学习就不适合了,时间就拉得太长了。 搞不好学了后面忘了前面。 并且实际工作中并不需要你去弄懂U-boot,会用就行:U-boot比驱动还复杂。 讲哪些内容? 怎么讲呢? 混着讲 比如先讲LED APP,知道APP怎么调用驱动,再讲LED硬件原理和裸机,最后讲驱动的编写。 这样可以快速掌握嵌入式Linux的整套开发流程, 不必像以前那样光学习裸机就花上1、2个月。 而里面的裸机课程,也会让你在掌握硬件操作的同时,把单片机也学会了。 讲基础技能 中断、休眠-唤醒、异步通知、阻塞、内存映射等等机制,会配合驱动和APP来讲解。 这些技能是嵌入式Linux开发的基础。 而这些驱动,只会涉及LED、按制、LCD等几个驱动。 掌握了这些输入、输出的驱动和对应的APP后,你已经具备基本的开发能力了。 讲配置 我们从厂家、从方案公司基本上都可以拿到一套完整的开发环境,怎么去配置它? 需要懂shell和python等配置脚本。 效果效率优先 以前我都是现场写代码、现场写文档,字写得慢,降低了学习效率。 这次,效果与效率统一考虑,不再追求所有东西都现场写。 容易的地方可先写好代码文档,难的地方现场写。 2.实战项目 会讲解这样的涉及linux网关/服务器相关项目(不限于,请多提建议):                      定位为:快速掌握项目开发经验,丰满简历。 涉及的每一部分都会讲,比如如果涉及蓝牙,在这里只会讲怎么使用,让你能写出程序;如果要深入,可以看后面的蓝牙专题。 3. 驱动大全 包括基础驱动、高级驱动。 这些驱动都是独立成章,深入讲解。 虽然基础驱动弱化了,但是作为Linux系统开发人员,这是必备技能,并且从驱动去理解内核是一个好方法。 在讲解这些驱动时,会把驱动的运行环境,比如内核调度,进程线程等概念也讲出来,这样就可以搭建一个知识体系。 没有这些知识体系的话,对驱动的理解就太肤浅了,等于在Linux框架下写裸机,一叶障目,不见泰山。 定位为:工具、字典,用到再学习。 4. 专题 想深入学习的任何内容,都可独立为专题。 比如U-boot专题、内核内存管理专题、systemtap调试专题。

      2000 人正在学习 去看看 韦东山

    https://www.ibm.com/developerworks/cn/linux/l-pcie/


    Linux 2.6.10内核下PCI Express Native热插拔框架的实现机制

    王兵 (bingwang_nudt@sina.com)国防科学技术大学计算机学院软件所

    2005 年 8 月 01 日

    PCI热插拔技术,可以有效避免由更换外设引起的服务器系统停机,对于提高服务器系统可用性和可扩展性意义重大。本文讨论了PCI Express热插拔所涉及的软件因素,并基于此,剖析了Linux 2.6.10内核下PCI Express 插槽热插拔子系统的关键实现机制。

    一 相关技术与研究

    1997年,PCI SIG制定了第一个PCI热插拔规范,其中定义了支持热插拔所必需的平台、板卡和软件元素。PCI SIG推出了标准热插拔控制器规范(SHPC SPEC),其中明确了热插拔的标准使用模式和严格的寄存器组要求,并且允许操作系统提供商在平台特定的软件之外提供热插拔支持,逐步完成了热插拔标准制定工作,进入技术的全面推广阶段。 2002年以后,Intel把热插拔作为一种天然属性赋予新推出的PCI Express规范,PCI Express热插拔总结了五年来工业标准的经验,具有如下特点:

    • 基于SHPC模式并对其进行了功能扩展;
    • PCI Express从设计上就把热插拔寄存器集成进入了其标准的性能寄存器组(而在SHPC 1.0中,热插拔寄存器是附加的);
    • 提供给操作系统一个统一的热插拔硬件寄存器接口,赋予了操作系统进行原生热插拔(绕过了传统基于BIOS的热插拔方法)的能力;
    • 通过在基本的体系机构层次定义热插拔必须的硬件要求,完善了一种标准的使用模式;
    • 提供了规格参数来保证OEM产品的低成本而高平台可靠性。

    具有完整功能的PCI Express热插拔系统在平台硬件和固件支持之外,还必须有操作系统以及设备驱动程序的支持。

    在各大芯片厂商纷纷推出支持PCI热插拔的产品的同时,Microsoft,Novell,SCO等公司在他们相关的操作系统中也都包括了支持热插拔的技术, Novell NetWare 4.11&5和SCO UnixWare 7以及更新版本支持完整PCI热插拔(热替换和热添加)。Microsoft Windows NT 4.0利用Compaq 服务器支持盘(SSD) for NT 4.0,提供了PCI热替换支持,进入2000年后,Microsoft Windows 2000也内建了对PCI热插拔技术的全面软件支持。对于GNU/Linux,从2001年1月份的内核2.4版本开始,PCI 热插拔开始成为其标准特性之一,到2.6 版本,热插拔功能已经被整合进入核心设备模型。现在,几乎所有的Linux发行版本,包括RedHat, Debian和 United Linux,都对PCI 热插拔提供了良好支持,并分别有所扩展。BSD分支在这方面起步较晚,OpenBSD在2004年三月份的3.6版本中才出现了不含设备驱动的设备热插拔参考框架,而FreeBSD 到5.3版本为止,尚未提供对PCI 热插拔的支持。

    作为2002年出现的规范,各个通用操作系统对PCI Express的支持才刚刚起步。在微软公司的 Windows 2000, Windows XP, and Windows Server 2003中,PCI Express设备被当作PCI设备进行热插拔,其他PCI Express的高级功能可以由固件激活。在Windows Longhorn中,才提供对PCI Express高级特性的原生支持。GNU/Linux在内核版本2.6.9中开始专门提供对于PCI Express 热插拔的支持,中间历经软件架构的改动,逐渐发展为以内核版本2.6.10以及内核版本2.6.12下两种不同的PCI Express热插拔驱动架构。本文主要讨论2.6.10所采用的PCI Express热插拔驱动架构,并在最后给出2.6.12的新特性。

    三 PCI Express 热插拔的软件支持

    根据规范,一个完整的 Native PCI Express 热插拔系统需要几方面的相互配合,分别为硬件元素、固件元素和软件元素。硬件元素是指主板总线系统的电气特性方面的支持,包括热插拔控制器(Hot-Plug Controller)、卡槽电源切换逻辑(Card Slot Power Switching Logic)、板卡重置逻辑(Card Reset Logic)、电源指示灯(Power Indicator)、提示按钮(Attention Button)和板卡存在检测引脚(Card Present Detect Pins)等等;固件元素是指主板BIOS必须对热插拔提供的支持,要实现Native PCI Express热插拔,固件必须提供OSHP方法或ACPI _OSC方法之一;软件元素是指操作系统操综合使用PCI Express 热插拔所必须提供的功能组件。

    软件元素

    为了操纵平台的硬件元素,提供PCI Express热插拔服务,我们必须实现表1所示的主要热插拔软件元素。

    PCI Express服务模型

    PCIE热插拔(PCIEHP)子系统中的标准热插拔系统驱动程序通过检查PCI Express性能数据结构中的插槽性能寄存器(Slot Capabilities register),来获取硬件的热插拔部件信息。这些寄存器所能反映的信息如下:

    1. 插槽是否支持热插拔
    2. 设备是否支持不通知软件的突然拔出操作
    3. 提示灯、控制器等硬件元素是否存在

    一旦软件完成了PCI Express设备热插拔功能的开启和配置工作,热插拔行为(例如移出或插入请求,电源故障)就可以向系统热插拔处理机制提交系统中断和电源管理事件。这种热插拔处理机制由操作系统独立提供,图1展示了PCI Express Native热插拔的服务模型,并展示了它与传统的基于ACPI的模型之间的区别。

    图1 PCI Express热插拔服务模型比较
    图1 PCI Express热插拔服务模型比较

    四Linux 2.6.10 PCIE 热插拔子系统代码分析

    热插拔框架

    为了提供PCI Express热插拔服务,Linux 2.6.10 PCIE热插拔(PCIEHP)子系统必须实现用户操作界面、热插拔服务程序和标准热插拔系统驱动等热插拔软件元素,并使软件的行为符合热插拔标准使用模式[4]。另外,一些特定的热插拔功能必须与支持热插拔的设备驱动结合在一起发挥作用。

    PCIEHP核内部分的主体是一个核心线程,其控制逻辑以模块的方式加载入内核,负责监控PCI Express总线上的热插拔事件,并做相关处理;核外部分是一个用户空间的脚本,从内核中调用,并根据内核传回的信息执行后续处理过程。

    1 热插拔驱动程序生命周期

    从PCIEHP子系统启动,到子系统被卸载期间,PCIEHP完全接管系统中PCI Express插槽热插拔事件。

    PCIEHP子系统启动时,首先要开启用户态守护进程。然后初始化通知机制,开启PCI Express热插拔事件处理核心线程,为操作系统中各总线数据结构分别预初始化一个热插拔插槽列表。接着,根据内核编译时是否指定ACPI式资源管理,初始化设备资源管理方式。最后,根据设备标识,在系统中注册PCI Express热插拔驱动程序,并将其绑定到总线上所有可能挂载热插拔插槽的PCIE桥接设备,然后初始化对应的热插拔控制器,分配资源,开启定时的中断轮询机制。

    卸载PCIEHP子系统,首先释放热插拔控制器全局列表中每个控制器所占用的资源,释放热插拔插槽全局列表中每个插槽所占用的资源,终止通知机制的运行,结束PCI Express热插拔事件处理核心线程;接着,根据内核编译时是否指定ACPI式资源管理,释放相关设备占用的系统资源;然后,注销PCI Express热插拔驱动程序;在处理完当前热插拔事件后,终止核外守护进程。

    在PCIEHP加载后,为了在热插拔执行过程中追踪其状态变化,我们把热插拔插槽的状态抽象如下:

    • 静态 (STATIC STATE ),
      正常工作或者空闲的时段,
    • 开启前闪烁提示态(BLINKINGON STATE)
      发出开启请求到确认之前的时段
    • 关闭前闪烁提示态 (BLINKINGOFF STATE)
      发出关闭请求到确认之前的时段
    • 开启态 (POWERON STATE)
      执行开启操作的时段
    • 关闭态(POWEROFF STATE)
      执行关闭操作的时段

    各个插槽状态之间的转换关系如图2所示:

    图2 插槽状态转换图
    图2 插槽状态转换图

    2 用户控制脚本/sbin/hotplug和核内外通信机制

    在内核热插拔处理完毕后,它会在核内调用一个用户态脚本hotplug,这个脚本将根据内核提供的信息进行后续处理工作。它是设备热插拔通告的用户空间部分,它接收内核传出的热插拔操作类型和环境变量,处理设备挂载和卸载操作。通常,hotplug会根据设备类型调用一个策略脚本,针对设备和系统当前参数进行后续的配置工作。例如,对于网卡,可能会调用脚本指定IP地址,网关和域名服务器等等,对于存储设备,可能会调用mount命令来加载它到文件系统内。

    内核传送给hotplug的信息包含热插拔操作类型(例如PCI),并且在一个环境变量中提供本次操作相关信息:

    • 行为种类:添加或删除
    • 对象设备所属PCI 类、子类和编程接口
    • 对象制造商标志和设备标志
    • 对象总线地址、插槽地址和功能函数编号

    核外的后续配置工作涉及如下文件:

    /etc/hotplug/pci.rc
    /ect/hotplug/pci.Agent
    /etc/rc.d/init.d/hotplug

    在内核中,由kernel/kmod.c中的函数

    int call_usermodehelper (char *path, char **argv, char **envp, int wait)来开启用户态脚本/sbin/hotplug。在参数表中,path表示了所启动的核外应用程序的路径,argv是应用程序的参数表,envp是环境变量列表,wait则指出了是否同步等待应用程序执行完毕再返回执行结果的状态。

    3 PCIE 热插拔模块构成

    为了使用PCI Express Native Hotplug,我们必须在编译的时候开启对应的功能模块。

    在内核配置中,PCIE Hotplug对应的开关为HOTPLUG_PCI_PCIE,它依赖于HOTPLUG_PCI。如果你的主板支持PCI Express Native Hotplug,可以选择Y;如果你只是想把这个驱动作为模块来编译,那么选择M,此模块叫做pciehp,在源代码\dirver\pci\hotplug\Kconfig文件中,你可以看到:

    config HOTPLUG_PCI_PCIE
    	tristate "PCI Express Hotplug driver"
    	depends on HOTPLUG_PCI

    完整的pciehp模块功能涉及到如下几个文件pci_hotplug_core.c, pciehp_core.c,pciehp_ctrl.c,pciehp_pci.cpciehp_hpc.c,另外根据是否开启了ACPI,包含pciehprm_acpi.c 或者pciehprm_nonacpi.c,在源代码\dirver\pci\hotplug\Makefile文件中,你可以看到:

    pci_hotplug-objs	:=	pci_hotplug_core.o
    pciehp-objs		:=	pciehp_core.o	\
    				pciehp_ctrl.o	\
    				pciehp_pci.o	\
    				pciehp_hpc.o
    ifdef CONFIG_ACPI_BUS
    	pciehp-objs += pciehprm_acpi.o
    else
    	pciehp-objs += pciehprm_nonacpi.o
    endif

    代码的主要任务就是在所有支持热插拔的PCIE桥上加载热插拔驱动程序,监控热插拔事件,并根据类型,如是热插入事件、热拔出事件还是电源故障等分别予以处理。

    热插拔驱动的加载

    热插拔驱动程序的加载所进行的主要工作是开启并初始化PCIE热插拔的内核线程,这部分代码位于/driver/pci/hotplug/pciehp_core.c中。入口函数为:

    static int __init pcied_init(void)

    #ifdef CONFIG_HOTPLUG_PCI_PCIE_POLL_EVENT_MODE
    	pciehp_poll_mode = 1;
    #endif
    	retval = pcie_start_thread();
    	if (retval)
    		goto error_hpc_init;
    	retval = pciehprm_init(PCI);
    	if (!retval) {
    		retval = pci_register_driver(&pcie_driver);
    		dbg("pci_register_driver = %d\n", retval);
    		info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
    	}

    pcied_init中所涉及的关键函数分析如下:

    retval = pcie_start_thread();

    初始化并开启通知机制:

    pciehp_event_start_thread()启动事件监控处理线程
    然后初始化slot列表,系统中每个bus给一个slot列表。
    struct pci_func *pciehp_slot_list[256]; 都设为NULL

    retval = pciehprm_init(PCI);

    原型:int pciehprm_init(enum php_ctlr_type ctlr_type)

    初始化资源(区别两种情况:acpi和非acpi的)在非acpi的初始化方式下,调用空函数legacy_pciehprm_init_pci();在pcihprm_nonacpi.h中,定义了irq_info,irq_routing_table两个结构。在acpi初始化方式下,通过pciehprm_acpi_scan_pci()在acpi树下遍历搜寻PCI设备。不论acpi和非acpi的,都要求php_ctlr_type为PCI。

    retval = pci_register_driver(&pcie_driver);

    注册并初始化PCI桥热插拔驱动程序模块。

    把设备驱动加入已注册设备驱动列表,即使在期间没有相应设备出现驱动程序仍然保持有效。

    	int count = 0;
    	/* initialize common driver fields */

    用来泛化之,可以把drv->driver看作为drv的基类信息

    	drv->driver.name = drv->name;
    	drv->driver.bus = &pci_bus_type;
    	drv->driver.probe = pci_device_probe;
    	drv->driver.remove = pci_device_remove;
    	drv->driver.kobj.ktype = &pci_driver_kobj_type;
    	pci_init_dynids(&drv->dynids);
    	count = driver_register(&drv->driver);

    注意:这里是如何从基类drv->driver一般设备的驱动向子类drv这个pci设备的驱动逆向关联的--使用CONTAINER宏

    pcie_driver为PCIE驱动程序对象,定义在 pciehp_core.c中

    	static struct pci_driver pcie_driver = {
    	//驱动名称定义为"pciehp"
    .name=	PCIE_MODULE_NAME, 
    // id_table指定探测函数probe所应用的范围,这里在表中指定为所有的PCI桥设备
    	.id_table	=	pcied_pci_tbl, 
    //probe为指定的设备探测函数
    	.probe	=	pcie_probe, 
    };
    static struct pci_device_id pcied_pci_tbl[] = {
    	{
    	//此处选择所有PCI桥
    .class =        ((PCI_CLASS_BRIDGE_PCI << 8) | 0x00),
     
    	.class_mask =	~0,
    	.vendor =       PCI_ANY_ID,
    	.device =       PCI_ANY_ID,
    	.subvendor =    PCI_ANY_ID,
    	.subdevice =    PCI_ANY_ID,
    	},

    设备探测pcie_probe:

    static int pcie_probe
    (struct pci_dev *pdev, const struct pci_device_id *ent)
    in pcie-core.c

    已经确定了pdev就是pcie_driver 所匹配的PCI桥设备,而且它在pcie_driver中所对应的设备特征号是*ent,就可以对其进行进一步的初始化和探测。

    具体行为如下:

    • 绑定热插拔插槽
    • 设置了控制器及其状态
    • 注册中断处理函数
    • 读写配置头,然后分配资源,最后对插槽检测,挂接

    热插拔事件的监控处理线程

    热插拔驱动程序对于热插拔事件的轮询和通知采用异步机制,对热插拔功能部件的操纵是通过读写相关寄存器组进行的。主要功能函数关系请参见图3:

    图3 插槽热插拔事件处理函数关系
    图3 插槽热插拔事件处理函数关系

    图3中主要函数的功能介绍如下:

    A插槽事件监控线程

    作为热插拔活动最直接的信息,插槽事件由硬件操作并置位相关寄存器组,系统软件可以通过定时轮询或者中断方式获取事件信息,执行对应的事件预处理函数。插槽事件如下:

    • 热插拔命令到达
    • 插槽锁状态改变
    • 适配卡存在状态改变
    • 电源出现故障

    php_ctlr->int_poll_timer.function = &int_poll_timeout其中php_ctlr是热插拔控制器状态php_ctlr_state_s类型,它定义于pciehp_hpc.h中,记录当前热插拔控制器重要状态,被用作HPC(controller)的控制器句柄;热插拔控制器controller位于文件pciehp.h中,描述了PCIE热插拔控制器的特征;

    定时轮询函数原型如下:

    static void int_poll_timeout(unsigned long lphp_ctlr)
    其中调用pcie_isr( 0, (void *)php_ctlr, NULL );
    当最后一个参数是NULL时,采用polling机制。
    static irqreturn_t pcie_isr(int IRQ, void *dev_id, struct pt_regs *regs);
    轮询机制通过定期(2second,使用一个计时器)读取ctrller对应的状态寄存器,来获取事件,然后调用ctrl状态参量对应的事件处理函数。也就是按钮、电源、MRL等等事件处理函数,并分别调用event_semaphore来激活event_thread.

    B事件预处理函数

    在插槽事件监控线程截获插槽事件后,它根据事件类型调用这组处理函数,执行完毕后,填写对应控制器上所挂接的事件队列,并激活睡眠等待的处理线程。可以激活睡眠中的处理线程的函数包括如下几个:

    pciehp_handle_attention_button :处理按钮事件
    pciehp_handle_switch_change:处理开关状态改变事件
    pciehp_handle_presence_change:处理存在性状态变化事件
    pciehp_handle_power_fault:处理电源故障事件
    pushbutton_helper_thread:按钮动作处理线程

    C热插拔事件处理核心线程event_thread

    event_thread的处理过程如下:
    在一个无限循环中,阻塞等待插槽事件发生
    当线程被某一事件唤醒后,
    如果 热插拔请求已通过延时确认
    进入热插拔请求处理函数
    否则
    轮询热插拔控制器队列:
    把控制器作为参数传给插槽事件处理函数
    其中

    if (pushbutton_pending)
    			pciehp_pushbutton_thread(pushbutton_pending);
    		else if (surprise_rm_pending)
    			pciehp_surprise_rm_thread(surprise_rm_pending);

    前一个处理按钮事件,后一个处理突然拔出事件。

    参数pushbutton_pending是由如下函数提供的:

    static void pushbutton_helper_thread(unsigned long data)
    {
             pushbutton_pending = data;
             up(&event_semaphore);

    而这个函数又被作为定时task事件的行动部分,赋值给

    static void interrupt_event_handler(struct controller *ctrl)中的
    p_slot->task_event.function:
    函数:
    p_slot->task_event.function
    = (void (*)(unsigned long)) pushbutton_helper_thread;
    参数:
    p_slot->task_event.data
    = (unsigned long) p_slot;

    D插槽事件处理函数interrupt_event_handler

    根据控制器信息,获取控制器对应插槽事件
    尚有事件等待处理,则执行以下内容:

    逐一查看控制器所挂接事件队列的每个成员(根据事件类型):

    1. 请求取消
    取消前五秒内触发的一次热插拔请求

    2. 请求触发
    触发一次热插拔操作:导致一个五秒钟的延时,如果五秒内请求没有被拒绝,则确认前一个热插拔请求

    3. 电源故障
    弹出提示信息

    4. 其他
    读取插槽的功能寄存器,更新状态信息。

    E热插拔请求处理函数pciehp_pushbutton_thread

    一个定期运行的程序,处理当前插槽上阻塞的请求,根据请求类型:对插槽中的设备进行热移出(F)或者热添加操作(G)。

    F插槽热拔出设备pciehp_disable_slot

    保证所除去的不是视频控制器
    卸载所除去的适配卡占用的系统资源,更新总线结构,关闭电源
    通知用户态守护进程
    更新插槽状态

    G插槽热添加设备pciehp_enable_slot

    1. 执行适配卡添加的一系列相关操作: 失败恢复预处理,存在性检验、打开电源,检查link training状态,获取设备基本信息,配置设备,为设备建立相关数据结构,挂接到上级总线等。

    2. 为新添加的设备查找并挂接驱动程序。

    3. 特别地,对于桥接设备,把它挂接到上级总线后,还要继续对其下级总线进行扫描和挂接。

    4. 通知用户态守护进程。

    5. 更新插槽状态。

    五 PCIE板卡热插拔的标准过程

    热插拔PCI板卡可以使用提示按钮或用户界面来进行,下面我们介绍使用用户界面来启动热插入和热拔出的操作过程,以及Fedora C4T2下所采用的方式。

    设备的热插入

    1. 操作员安装卡,闭合插槽保护锁,保护锁感应器通知热插拔控制器把连接信号接通到插槽。

    2. 然后,操作员通知热插拔服务程序:卡已经被安装并可以激活。软件提示用户对此进行确认。

    3. 在操作员请求连接后,热插拔服务程序向控制着热插拔控制器的热插拔系统驱动程序下达命令,闪烁插槽的电源指示灯,提示操作员此时不可以拔动适配卡。

    4. 在热插拔软件对此请求进行确认期间内,电源指示灯继续闪烁。注意此时软件可能会拒绝这个安装请求(例如,安全策略此刻禁止插槽被激活)。另外,如果请求没有生效,软件拒绝请求并对热插拔控制器发出命令关闭电源指示灯。规范建议软件通过一条消息通知操作员请求被拒绝的原因。

    5. 如果请求被确认,热插拔服务程序对热插拔系统驱动发出请求,为插槽加电。

    6. 加电后,软件发出命令完全打开电源指示灯。

    7. 当link training完成后,操作系统指示平台配置程序赋予适配卡必需的资源,来配置适配卡的功能。

    8. 操作系统为PCI Express设备中的功能寻找恰当的驱动程序,并加载之。

    9. 接着系统调用驱动程序的初始化入口,并执行驱动的初始化代码。这些代码完成设备的设置,并填写设备的PCI 配置命令寄存器的相关标志位来激活设备。

    热移出设备

    1. 操作员通过指定适配卡所在物理插槽号码来初始化移出请求。

    2. 软件弹出窗口要求操作员确认请求。注意,此时电源指示灯保持开启状态。

    3.操作员确认请求后,热插拔服务程序向热插拔系统驱动发出请求,要求热插拔控制器闪烁电源指示灯。注意此时软件可能会拒绝这个移出请求(例如,适配卡目前正被关键系统功能所使用)。另外,如果请求没有被确认,软件将拒绝请求并对热插拔控制器发出命令,重新开启电源指示灯。规范建议软件通过一条消息通知操作员请求被拒绝的原因。

    4. 如果请求被确认,热插拔服务程序将命令适配卡的设备驱动保持静默,也就是说驱动一方面必须停止向适配卡发出请求,另一方面必须完成或者终止所有已经发出的请求,并禁止适配卡产生新的事务(包括中断)。

    5. 软件发出命令,通过在插槽所连接的根端口或交换端口中的链接控制寄存器禁掉适配卡的链接。这使得链接两侧的端口均被禁止。

    6. 软件指示热插拔控制器禁掉插槽。

    7. 成功切断电源后,软件发出关闭电源指示灯命令。指示灯熄灭后,操作员可以开始安全地从插槽移出适配卡。打开插槽安全锁,热插拔控制器从插槽上撤除所有的信号(例如SMBus 和Vaux),此时卡可以被移出。

    8. 操作系统释放内存空间,I/O空间,中断线等曾经属于该设备的系统资源。

    Fedora下所采用热插拔实现的方式

    如果在最近一次编译中选择了PCI Express 热插拔功能,而且驱动是以模块方式存在,那么,可以在命令行下键入以下内容:

    modprobe pciehp

    如果驱动成功,则可以在/sys/bus/pci/slots/下面发现以可热插拔插槽编号命名的目录,进入相关目录,可以进行下一步操作。

    echo 1 >power开启某个插槽上的电源 ,进行热插入

    echo 0 >power关闭某个插槽上的电源,执行热拔出

    若不能加载pciehp驱动,一般是由于硬件不支持或者固件缺少OSHP方法或ACPI _OSC方法之一。

    六 linux2.6.12中PCIE驱动模型的变化

    在linux2.6.10中,Linux驱动程序模型要求物理设备被单独的驱动程序独占访问。 PCI Express端口是一个拥有许多独立功能的PCI-PCI桥设备,作为一个简洁的方案,每个功能要分别实现其自己的驱动程序,但是这样造成了多个驱动程序在唯一的PCI-PCI桥设备中出现竞争的状况。也就是说,虽然PCI Express提供了如Power Management (PME)、 Advanced Error Reporting (AER)、 Hot-Plug (HP) 和Virtual Channel (VC) access等多种功能,但是如果某个PCI Express端口的native hotplug 驱动程序加载后,它就会独占这个PCI-PCI桥的端口,内核就不能再于其上加载其他功能的驱动程序了。为解决这个问题,在Linux内核版本2.6.12中,PCI Express的热插拔又有所改变,提出了PCI Express 端口总线驱动程序(PCI Express Port Bus Driver)的概念。

    在实现上,PCI Express 端口总线驱动程序管理主板上的所有PCI Express 端口,并且把所有提供的服务请求发送到对应的服务驱动程序上。其优点概括如下:

    • 允许多个服务驱动程序在一个PCI-PCI桥端口设备上同步运行。
    • 允许各个服务驱动程序相互不受干扰地独立实现。
    • 允许一个服务驱动程序在多个PCI-PCI桥端口设备上运行。
    • 管理PCI-PCI桥端口设备资源并分发它们到发出请求的服务驱动程序。

    例如,在注册热插拔驱动程序时,不再使用pci_register_driver直接向系统注册,而是使用int pcie_port_service_register(struct pcie_port_service_driver *new) 向端口总线驱动程序注册;在注销热插拔驱动程序时,不再使用pci_unregister_driver直接向系统注销,而是使用void pcie_port_service_unregister(struct pcie_port_service_driver *new)来向端口总线驱动程序注销;而端口总线驱动程序是直接注册注销到系统的。

    目前,PCI Express 端口总线驱动程序模型还在发展演变中。

    七 评估与总结

    根据可用性理论,系统的可用度可以使用下列公式来计算:

    在高负荷运转的服务器上,商用可靠性的元件的故障更换并非小概率事件,对于更换故障元件和升级配件这样的事件,没有热插拔支持的系统必须停机断电进行处理,而具备热插拔支持的系统则仅仅需要很少的软件切换和拔插时间开销。从上面的公式我们可以看出,MTTR值越小,系统的可用性就越高。我们定义了一个比例因子V,V= MTTRnoHP/MTTRHP,根据业界经验,V的取值一般在10-100之间。描述可用性的一种常用的方法是使用"9"。如三个 9 表示 99.9% 的可用性,它表示一年大约有 8.5 小时的服务中断期。四个 9 (99.99%) 是更高一级的可用性,表示一年大约有 1 小时的服务中断期。根据可用度公式计算,在单个元件可靠度不变的情况下,Linux操作系统对PCI Express热插拔的支持,可以使服务器系统的外设相关可用性跃升一个等级(1个9),同时,PCI Express热插拔技术使得在线更换和升级PCI Express外设板卡成为可能,这使系统获得了良好的可扩展性。

    综上所述,本文讨论了PCI Express热插拔所涉及的软件因素,分析了linux2.6.10的PCI Express插槽热插拔功能PCIEHP子系统,并对热插拔支持在提高服务器系统外设相关可用性的作用进行了定量的分析。为了继续提高操作系统可用性和可扩展性支持能力,Linux PCI Express hotplug以下方面还有待发展:继续完善热插拔架构的开放性,以提供完整统一的接口供驱动开发人员编写其他设备的热插拔支持模块;在插槽热插拔之外,提供对Server IO Module(SIOM)热插拔的支持;完善热替换和热升级技术。这些问题都是非常具有挑战性的。

    参考资料

    1. PCI Express Base Specification Revision 1.0
    2. Linux Device Driver III edition O'Reilly出版社 2005
    3. The Compaq PCI Hotplug Driver for Linux
    4. PCI Express System Architecture Addison-Wesley 出版社 Sep 04, 2003
    5. The PCI Express Port Bus Driver Guide HOWTO Tom L Nguyen tom.l.nguyen@intel.com
    6. Introduction to PCI Express A Hardware and Software Developer's Guide Adam H. Wilen Justin
    7. P. Schade Ron Thornburg
    8. www.sourceforge.org
    9. www.openbsd.org
    10. www.kernel.org

    2020-01-07 14:32:12 u010443710 阅读数 21
    • 韦东山升级版嵌入式视频之快速入门

      韦东山老师为啥要录升级版嵌入式视频? 200x年左右,嵌入式Linux在全世界、在中国刚刚兴起。 我记得我2005年进入中兴时,全部门的人正在努力学习Linux。 在2008年,我写了一本书《嵌入式Linux应用开发完全手册》。 它的大概内容是:裸机、U-boot、Linux内核、Linux设备驱动。 那时还没有这样讲解整个系统的书, 芯片厂家Linux开发包也还不完善,从bootloader到内核,再到设备驱动都不完善。 有全系统开发能力的人也很少。 于是这书也就恰逢其时,变成了畅销书。 我也根据这个思路录制了视频:裸机、U-boot、Linux内核、Linux设备驱动。 收获些许名声,带领很多人进入Linux世界。 11年过去了,嵌入式Linux世界发生了翻天覆地的变化 ① 基本系统能用 芯片厂家都会提供完整的U-boot、Linux内核、芯片上硬件资源的驱动。 方案厂家会做一些定制,比如加上某个WIFI模块,会添加这个WIFI模块的驱动。 你可以使用厂家的原始方案,或是使用/借鉴方案商的方案,做出一个“能用”的产品。 ② 基础驱动弱化;高级驱动专业化 基础的驱动,比如GPIO、UART、SPI、I2C、LCD、MMC等,有了太多的书籍、视频、示例代码,修修改改总是可以用的。 很多所谓的驱动工程师,实际上就是“调参工程师”。 我们群里有名的火哥,提出了一个概念:这些驱动就起一个“hardware enable”的作用。 高级的驱动,比如USB、PCIE、HDMI、MIPI、GPU、WIFI、蓝牙、摄像头、声卡。 体系非常复杂,很少有人能讲清楚,很多时候只是一笔带过。 配置一下应用层工具就了事,能用就成。 这些高级驱动,工作中需要专门的人来负责,非常专业。 他们是某一块的专家,比如摄像头专家、音频专家。 ③ 项目为王 你到一个公司,目的是把产品做出来,会涉及APP到内核到驱动全流程。 中小公司玩不起华为中兴的配置,需要的是全面手。 大公司里,只负责很小很小一块的镙丝钉,位置也不太稳固啊。 所以,如果你不是立志成为某方面的专家,那就做一个全栈工程师吧。 ④ 调试很重要 都说代码是3分写7分调,各种调试调优技术,可以为你的升职加薪加一把火。 基于上述4点,我录制的全新视频将有这些特点: 1. 快速入门, 2. 实战项目, 3. 驱动大全, 4. 专题, 5. 授人以渔, 6. 要做任务 另外,我们会使用多款芯片同时录制,先讲通用的原理,再单独讲各个板子的操作。 这些芯片涵盖主流芯片公司的主流芯片,让你学习工作无缝对接。 1.快速入门 入门讲究的是快速,入门之后再慢慢深入, 特别是对于急着找工作的学生,对于业余时间挑灯夜读的工作了的人,一定要快! 再从裸机、U-boot、内核、驱动这样的路线学习就不适合了,时间就拉得太长了。 搞不好学了后面忘了前面。 并且实际工作中并不需要你去弄懂U-boot,会用就行:U-boot比驱动还复杂。 讲哪些内容? 怎么讲呢? 混着讲 比如先讲LED APP,知道APP怎么调用驱动,再讲LED硬件原理和裸机,最后讲驱动的编写。 这样可以快速掌握嵌入式Linux的整套开发流程, 不必像以前那样光学习裸机就花上1、2个月。 而里面的裸机课程,也会让你在掌握硬件操作的同时,把单片机也学会了。 讲基础技能 中断、休眠-唤醒、异步通知、阻塞、内存映射等等机制,会配合驱动和APP来讲解。 这些技能是嵌入式Linux开发的基础。 而这些驱动,只会涉及LED、按制、LCD等几个驱动。 掌握了这些输入、输出的驱动和对应的APP后,你已经具备基本的开发能力了。 讲配置 我们从厂家、从方案公司基本上都可以拿到一套完整的开发环境,怎么去配置它? 需要懂shell和python等配置脚本。 效果效率优先 以前我都是现场写代码、现场写文档,字写得慢,降低了学习效率。 这次,效果与效率统一考虑,不再追求所有东西都现场写。 容易的地方可先写好代码文档,难的地方现场写。 2.实战项目 会讲解这样的涉及linux网关/服务器相关项目(不限于,请多提建议):                      定位为:快速掌握项目开发经验,丰满简历。 涉及的每一部分都会讲,比如如果涉及蓝牙,在这里只会讲怎么使用,让你能写出程序;如果要深入,可以看后面的蓝牙专题。 3. 驱动大全 包括基础驱动、高级驱动。 这些驱动都是独立成章,深入讲解。 虽然基础驱动弱化了,但是作为Linux系统开发人员,这是必备技能,并且从驱动去理解内核是一个好方法。 在讲解这些驱动时,会把驱动的运行环境,比如内核调度,进程线程等概念也讲出来,这样就可以搭建一个知识体系。 没有这些知识体系的话,对驱动的理解就太肤浅了,等于在Linux框架下写裸机,一叶障目,不见泰山。 定位为:工具、字典,用到再学习。 4. 专题 想深入学习的任何内容,都可独立为专题。 比如U-boot专题、内核内存管理专题、systemtap调试专题。

      2000 人正在学习 去看看 韦东山

    linux下可通过/sys/bus/pci/devices/0000\:[bus number]\:[device number].[function number]/ 目录下的节点进行热拔插操作。

    板子上电前PCIe插槽有一块NVME的固态硬盘

    [    0.198515] pci 0000:00:00.0: [16c3:abcd] type 01 class 0x060400
    [    0.199284] pci 0000:01:00.0: [126f:2263] type 00 class 0x010802
    [    8.161374] nvme nvme0: pci function 0000:01:00.0
    [    8.166407] nvme 0000:01:00.0: enabling device (0000 -> 0002)
    
    # ls /sys/bus/pci/devices/0000\:01\:00.0
    broken_parity_status      enable                    rescan
    class                     irq                       reset
    config                    local_cpulist             resource
    consistent_dma_mask_bits  local_cpus                subsystem
    device                    modalias                  subsystem_device
    devspec                   msi_bus                   subsystem_vendor
    dma_mask_bits             power                     uevent
    driver_override           remove                    vendor

    现在需要在板子保持开机运行的状态下,先取下NVME的固态硬盘,再换上一块PCIe 网卡,并让网卡正常工作。

    • remove 原有设备,然后lspci查看, 0000:01:00.0: [126f:2263]已经不见了
    # echo 1 > /sys/bus/pci/devices/0000\:01\:00.0/remove
    #
    # lspci
    00:00.0 Class 0604: 16c3:abcd
    • 现在可以从插槽取下固态硬盘,插上网卡
    • 通过上游bus进行rescan
     echo 1 > /sys/bus/pci/devices/0000\:00\:00.0/rescan
    [   72.436704] pci 0000:00:00.0: bridge configuration invalid ([bus 00-00]), reconfiguring
    [   72.445041] pci 0000:01:00.0: [10ec:8168] type 00 class 0x020000
    [   72.451319] pci 0000:01:00.0: reg 0x10: initial BAR value 0x00000000 invalid
    [   72.458380] pci 0000:01:00.0: reg 0x10: [io  size 0x0100]
    [   72.464113] pci 0000:01:00.0: reg 0x18: [mem 0x00000000-0x00000fff 64bit]
    [   72.471112] pci 0000:01:00.0: reg 0x20: [mem 0x00000000-0x00003fff 64bit]
    [   72.479299] pci 0000:01:00.0: supports D1 D2
    [   72.483583] pci 0000:01:00.0: PME# supported from D0 D1 D2 D3hot D3cold
    [   72.490667] pci_bus 0000:01: busn_res: [bus 01] end is updated to 01
    [   72.497090] pci 0000:00:00.0: BAR 7: assigned [io  0x1000-0x1fff]
    [   72.503202] pci 0000:01:00.0: BAR 4: assigned [mem 0xf1100000-0xf1103fff 64bit]
    [   72.510683] pci 0000:01:00.0: BAR 2: assigned [mem 0xf1104000-0xf1104fff 64bit]
    [   72.518162] pci 0000:01:00.0: BAR 0: assigned [io  0x1000-0x10ff]
    # [   72.569018] r8169 0000:01:00.0: no of_node; not parsing pinctrl DT
    [   72.575313] r8169 Gigabit Ethernet driver 2.3LK-NAPI loaded
    [   72.580996] r8169 0000:01:00.0: enabling device (0000 -> 0003)
    [   72.603261] r8169 0000:01:00.0 eth1: RTL8168h/8111h at 0xc8ea0000, e8:4e:06:72:f5:8a, XID 14100800 IRQ 66
    [   72.612886] r8169 0000:01:00.0 eth1: jumbo features [frames: 9200 bytes, tx checksumming: ko]
    [   72.846167] r8169 0000:01:00.0: Direct firmware load for rtl_nic/rtl8168h-2.fw failed with error -2
    [   72.855262] r8169 0000:01:00.0 eth1: unable to load firmware patch rtl_nic/rtl8168h-2.fw (-2)
    [   72.876122] r8169 0000:01:00.0 eth1: link down
    

     

    • 查看新的网卡设备,多了一个eth1
    # lspci
    00:00.0 Class 0604: 16c3:abcd
    01:00.0 Class 0200: 10ec:8168
    #
    #
    # ifconfig
    eth0      Link encap:Ethernet  HWaddr 02:67:AB:81:02:BB
              UP BROADCAST MULTICAST  MTU:1500  Metric:1
              RX packets:0 errors:0 dropped:0 overruns:0 frame:0
              TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:1000
              RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)
              Interrupt:16
    
    eth1      Link encap:Ethernet  HWaddr E8:4E:06:72:F5:8A
              UP BROADCAST MULTICAST  MTU:1500  Metric:1
              RX packets:0 errors:0 dropped:0 overruns:0 frame:0
              TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:1000
              RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)
    
    lo        Link encap:Local Loopback
              inet addr:127.0.0.1  Mask:255.0.0.0
              UP LOOPBACK RUNNING  MTU:65536  Metric:1
              RX packets:0 errors:0 dropped:0 overruns:0 frame:0
              TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:1
              RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

     

    2019-02-24 22:36:31 scarecrow_byr 阅读数 295
    • 韦东山升级版嵌入式视频之快速入门

      韦东山老师为啥要录升级版嵌入式视频? 200x年左右,嵌入式Linux在全世界、在中国刚刚兴起。 我记得我2005年进入中兴时,全部门的人正在努力学习Linux。 在2008年,我写了一本书《嵌入式Linux应用开发完全手册》。 它的大概内容是:裸机、U-boot、Linux内核、Linux设备驱动。 那时还没有这样讲解整个系统的书, 芯片厂家Linux开发包也还不完善,从bootloader到内核,再到设备驱动都不完善。 有全系统开发能力的人也很少。 于是这书也就恰逢其时,变成了畅销书。 我也根据这个思路录制了视频:裸机、U-boot、Linux内核、Linux设备驱动。 收获些许名声,带领很多人进入Linux世界。 11年过去了,嵌入式Linux世界发生了翻天覆地的变化 ① 基本系统能用 芯片厂家都会提供完整的U-boot、Linux内核、芯片上硬件资源的驱动。 方案厂家会做一些定制,比如加上某个WIFI模块,会添加这个WIFI模块的驱动。 你可以使用厂家的原始方案,或是使用/借鉴方案商的方案,做出一个“能用”的产品。 ② 基础驱动弱化;高级驱动专业化 基础的驱动,比如GPIO、UART、SPI、I2C、LCD、MMC等,有了太多的书籍、视频、示例代码,修修改改总是可以用的。 很多所谓的驱动工程师,实际上就是“调参工程师”。 我们群里有名的火哥,提出了一个概念:这些驱动就起一个“hardware enable”的作用。 高级的驱动,比如USB、PCIE、HDMI、MIPI、GPU、WIFI、蓝牙、摄像头、声卡。 体系非常复杂,很少有人能讲清楚,很多时候只是一笔带过。 配置一下应用层工具就了事,能用就成。 这些高级驱动,工作中需要专门的人来负责,非常专业。 他们是某一块的专家,比如摄像头专家、音频专家。 ③ 项目为王 你到一个公司,目的是把产品做出来,会涉及APP到内核到驱动全流程。 中小公司玩不起华为中兴的配置,需要的是全面手。 大公司里,只负责很小很小一块的镙丝钉,位置也不太稳固啊。 所以,如果你不是立志成为某方面的专家,那就做一个全栈工程师吧。 ④ 调试很重要 都说代码是3分写7分调,各种调试调优技术,可以为你的升职加薪加一把火。 基于上述4点,我录制的全新视频将有这些特点: 1. 快速入门, 2. 实战项目, 3. 驱动大全, 4. 专题, 5. 授人以渔, 6. 要做任务 另外,我们会使用多款芯片同时录制,先讲通用的原理,再单独讲各个板子的操作。 这些芯片涵盖主流芯片公司的主流芯片,让你学习工作无缝对接。 1.快速入门 入门讲究的是快速,入门之后再慢慢深入, 特别是对于急着找工作的学生,对于业余时间挑灯夜读的工作了的人,一定要快! 再从裸机、U-boot、内核、驱动这样的路线学习就不适合了,时间就拉得太长了。 搞不好学了后面忘了前面。 并且实际工作中并不需要你去弄懂U-boot,会用就行:U-boot比驱动还复杂。 讲哪些内容? 怎么讲呢? 混着讲 比如先讲LED APP,知道APP怎么调用驱动,再讲LED硬件原理和裸机,最后讲驱动的编写。 这样可以快速掌握嵌入式Linux的整套开发流程, 不必像以前那样光学习裸机就花上1、2个月。 而里面的裸机课程,也会让你在掌握硬件操作的同时,把单片机也学会了。 讲基础技能 中断、休眠-唤醒、异步通知、阻塞、内存映射等等机制,会配合驱动和APP来讲解。 这些技能是嵌入式Linux开发的基础。 而这些驱动,只会涉及LED、按制、LCD等几个驱动。 掌握了这些输入、输出的驱动和对应的APP后,你已经具备基本的开发能力了。 讲配置 我们从厂家、从方案公司基本上都可以拿到一套完整的开发环境,怎么去配置它? 需要懂shell和python等配置脚本。 效果效率优先 以前我都是现场写代码、现场写文档,字写得慢,降低了学习效率。 这次,效果与效率统一考虑,不再追求所有东西都现场写。 容易的地方可先写好代码文档,难的地方现场写。 2.实战项目 会讲解这样的涉及linux网关/服务器相关项目(不限于,请多提建议):                      定位为:快速掌握项目开发经验,丰满简历。 涉及的每一部分都会讲,比如如果涉及蓝牙,在这里只会讲怎么使用,让你能写出程序;如果要深入,可以看后面的蓝牙专题。 3. 驱动大全 包括基础驱动、高级驱动。 这些驱动都是独立成章,深入讲解。 虽然基础驱动弱化了,但是作为Linux系统开发人员,这是必备技能,并且从驱动去理解内核是一个好方法。 在讲解这些驱动时,会把驱动的运行环境,比如内核调度,进程线程等概念也讲出来,这样就可以搭建一个知识体系。 没有这些知识体系的话,对驱动的理解就太肤浅了,等于在Linux框架下写裸机,一叶障目,不见泰山。 定位为:工具、字典,用到再学习。 4. 专题 想深入学习的任何内容,都可独立为专题。 比如U-boot专题、内核内存管理专题、systemtap调试专题。

      2000 人正在学习 去看看 韦东山

    qemu里pci设备的热插拔

    -v0.1 2019.2.17 Sherlock Init
    -v0.2 2019.2.21 Sherlock Add all related command

    本文讨论linux系统中pci设备直通给qemu中涉及的pci热插拔问题。

    场景是一个pcie设备的vf通过vfio直通给qemu使用,这时如果我们在host上通过sysfs把
    对应的vf disable掉。

    正常来讲,qemu里的vf pci设备会表现为一个pci设备热拔出的行为。与之相对应的设置为:

    1. guest kernel的配置里要打开pci hotplug: CONFIG_HOTPLUG_PCI_PCIE.

    2. guest kernel的启动cmdline里要是能pci native hotplug, 加上pcie_port=native

    3. 启动qemu的时候,需要把直通上来的pci vf挂到一个支持pci热插拔的pci桥下面:
      比如在qemu里挂接一个ioh3420的pci桥,然后再把直通的vf挂在这个桥下。

    4. 本文的测试是在主线linux v5.0-rc6上做的,这个版本有一个pci hotplug的bug,这个
      bug会导致虚拟机里vf无法被热拔。相关的fix补丁已经被pci maintainer ack, 会合
      入v5.1主线版本。如果是在v5.0, 以及之前的内核的版本上测试,需要确认这个补丁
      是否合入:
      [PATCH RESEND] PCI: pciehp: Assign ctrl->slot_ctrl before writing it to hardware

    综合以上,如下的qemu启动命令,配合正确的kernel,可以支持qemu里直通vf的pci热拔
    操作:(这里已ARM64平台为例)

    	qemu-system-aarch64 -machine virt,gic_version=3 -enable-kvm -cpu host \
    	-m 1024 -kernel ./Image -initrd ./minifs.cpio.gz -nographic -append \
    	"rdinit=init console=ttyAMA0 earlycon=pl011,0x9000000 pcie_ports=native" \
    	-device ioh3420,id=root_port \
    	-device vfio-pci,host=0000:75:00.1,bus=root_port
    

    具体可以这样测试:(已HiSilicon D06 zip engine为例)

    1. 在host上把vf和vfio驱动绑定:
    	echo 1 > /sys/devices/pci0000:74/0000:74:00.0/0000:75:00.0/sriov_numvfs
    	echo 0000:75:00.1 > /sys/bus/pci/drivers/hisi_zip/unbind
    	echo vfio-pci > /sys/devices/pci0000:74/0000:74:00.0/0000:75:00.1/driver_override
    	echo 0000:75:00.1 > /sys/bus/pci/drivers_probe
    
    1. 启动qemu: 同上面的命令

    2. 在host上disable vf:

    	echo 0 > /sys/devices/pci0000:74/0000:74:00.0/0000:75:00.0/sriov_numvfs
    

    可以看到在qemu里,vf表现为一个pci热拔的动作:

        (fix me: add log, 目前看上面的命令会在host上挂住)
    

    为了使得这篇介绍完整,对于qemu里pci设备的热插,可以这样来做:

    1. 启动qemu后按ctrl a + c 进入qemu monitor(启动qemu的时候带ioh3420但是不带VF设备)

    2. 在qemu monitor里: device_add vfio-pci,host=0000:75:00.1,bus=root_port

    这样可以把已经和vfio驱动绑定的VF PCI热插到qemu
    (fix me: lspci看不到新设备,但是在qemu monitor里info pci可以看到新插入的设备)

    存储基础知识

    阅读数 197

    没有更多推荐了,返回首页