Linux那些事儿之我是Hub(27)电源管理的四大消息
如果真的有一种水可以让你让我喝了不会醉那么也许有一种泪可以让你让我流了不伤悲如果真的有一种硬件可以让你让我用了不耗电那么也许有一种代码可以让你让我看了不得不崩溃这一节涉及电源管理中的一些核心概念,所以你如果可以选择看,也可以选择不看,it’s up to you.883行,令dev.power.power_state.event等于msg.event.现在是时候来讲一讲两样东西了,一个
如果真的有一种水
可以让你让我喝了不会醉
那么也许有一种泪
可以让你让我流了不伤悲
如果真的有一种硬件
可以让你让我用了不耗电
那么也许有一种代码
可以让你让我看了不得不崩溃
这一节涉及电源管理中的一些核心概念,所以你如果可以选择看,也可以选择不看,it’s up to you.
883行,令dev.power.power_state.event等于msg.event.现在是时候来讲一讲两样东西了,一个是msg,一个是event.细心的你一定注意到连续几个函数的第二个参数都是pm_message_t msg,它姓甚名谁,是何许人也?来自include/linux/pm.h,是电源管理部分的一个很重要的结构体,
202 typedef struct pm_message {
203 int event;
204 } pm_message_t;
这个结构体也够酷,居然只有一个成员,那就是event.很显然pm_message的意思就是PM core传递给设备驱动的消息,事实上suspend有好几种境界,比如一种是简单的停止驱动并且把设备设置为低功耗的状态,一种是停止驱动但并不真正的把设备设置为低功耗的状态,(比如因为你实际上只想做一次snapshot,或者叫做一次系统内存image快照,而并不需要真正的把设备持续挂起),还有一种更复杂的状态叫做PRETHAW,稍后会说.那么你说PM core部分如何让设备驱动知道你想进入哪种境界?有这么一个参数来传达这种思想不就Ok了么?关于这个event,我们来看include/linux/pm.h中的介绍,
206 /*
207 * Several driver power state transitions are externally visible, affecting
208 * the state of pending I/O queues and (for drivers that touch hardware)
209 * interrupts, wakeups, DMA, and other hardware state. There may also be
210 * internal transitions to various low power modes, which are transparent
211 * to the rest of the driver stack (such as a driver that's ON gating off
212 * clocks which are not in active use).
213 *
214 * One transition is triggered by resume(), after a suspend() call; the
215 * message is implicit:
216 *
217 * ON Driver starts working again, responding to hardware events
218 * and software requests. The hardware may have gone through
219 * a power-off reset, or it may have maintained state from the
220 * previous suspend() which the driver will rely on while
221 * resuming. On most platforms, there are no restrictions on
222 * availability of resources like clocks during resume().
223 *
224 * Other transitions are triggered by messages sent using suspend(). All
225 * these transitions quiesce the driver, so that I/O queues are inactive.
226 * That commonly entails turning off IRQs and DMA; there may be rules
227 * about how to quiesce that are specific to the bus or the device's type.
228 * (For example, network drivers mark the link state.) Other details may
229 * differ according to the message:
230 *
231 * SUSPEND Quiesce, enter a low power device state appropriate for
232 * the upcoming system state (such as PCI_D3hot), and enable
233 * wakeup events as appropriate.
234 *
235 * FREEZE Quiesce operations so that a consistent image can be saved;
236 * but do NOT otherwise enter a low power device state, and do
237 * NOT emit system wakeup events.
238 *
239 * PRETHAW Quiesce as if for FREEZE; additionally, prepare for restoring
240 * the system from a snapshot taken after an earlier FREEZE.
241 * Some drivers will need to reset their hardware state instead
242 * of preserving it, to ensure that it's never mistaken for the
243 * state which that earlier snapshot had set up.
244 *
245 * A minimally power-aware driver treats all messages as SUSPEND, fully
246 * reinitializes its device during resume() -- whether or not it was reset
247 * during the suspend/resume cycle -- and can't issue wakeup events.
248 *
249 * More power-aware drivers may also use low power states at runtime as
250 * well as during system sleep states like PM_SUSPEND_STANDBY. They may
251 * be able to use wakeup events to exit from runtime low-power states,
252 * or from system low-power states such as standby or suspend-to-RAM.
253 */
254
255 #define PM_EVENT_ON 0
256 #define PM_EVENT_FREEZE 1
257 #define PM_EVENT_SUSPEND 2
258 #define PM_EVENT_PRETHAW 3
259
260 #define PMSG_FREEZE ((struct pm_message){ .event = PM_EVENT_FREEZE, })
261 #define PMSG_PRETHAW ((struct pm_message){ .event = PM_EVENT_PRETHAW, })
262 #define PMSG_SUSPEND ((struct pm_message){ .event = PM_EVENT_SUSPEND, })
263 #define PMSG_ON ((struct pm_message){ .event = PM_EVENT_ON, })
貌似一大段,其实就是定义了八个宏,不过这八个宏咱们在usb子系统里面都会遇到,只是早晚的事情.我们慢慢来说,首先,message,即消息,也被叫做事件,即event,于是每条具体的消息最终被道上的兄弟以一个叫做事件码的东西区分,即event code.而当前Linux内核中,一共有四种事件码,它们是:
ON,或者叫PM_EVENT_ON,这个事件码实际上是不会用来传达消息的,倒是经常用来表征设备当前所处于的一种状态,即,告诉世界,本设备当前并没有处于挂起的状态,咱目前一切正常.我们这里看到的这个dev.power.power_state.event就是用来记录设备的这个状态的,如果没有挂起,那么这个值就应该是PM_EVENT_ON.
EVENT_SUSPEND,或曰PM_EVENT_SUSPEND,这个不用说了,传达这个消息的意思就是想让设备进入低功耗的状态,用David Brownell在Documentation/power/devices.txt文件中原话来说,就是put hardware into a low-power state.而我们这里的赋值令dev.power.power_state.event等于msg.event.这就很好理解了,在调用了设备驱动的suspend函数去执行实际的任务之后,为设备记录下这个事件,从此以后你这个设备就不再是处于PM_EVENT_ON的状态了,你已经挂起了.
EVENT_FREEZE,或曰PM_EVENT_FREEZE,和EVENT_SUSPEND的区别咱们前面也说了,这里注释也说了,总之就是挂起的境界不太一样,毕竟其挂起的目的也不一样.
EVENT_PRETHAW,或曰PM_EVENT_PRETHAW,这就属于挂起的另一种境界.这一事件类型是为了支持STD的.确切地说,2.6内核实现了一种叫做swsusp的STD方法.(swsusp就是Software Suspend的意思.)而当时David Brownell在Linux中引入这么一个宏的目的就是为了支持swsusp的snapshot image恢复,这件事情的缘由是,我们知道软件挂起需要做系统内存映像快照,然后要恢复这个快照,然而,对于某些设备来说,直接恢复这个快照会导致错误,因为resume()函数通常会读硬件的状态,然后它会认为硬件的这种状态是被suspend函数设置的,或者另一种情况是由于掉电重起而复位(reset)的,然而swsusp比较变态,它会像kexec一样,先使用另一个小内核实例,然后load那个snapshot,从而切换成新的内核.而问题出在哪里呢?问题就在于swsusp会在load snapshot之前把硬件们都suspend,于是硬件们将进入某个状态,请你注意了,由于这时候那个snapshot内核还没有加载,所以这时候硬件们进入的这个状态只能说是不三不四的状态.(注:kexec是Eric Biederman的作品,其作用是让您可以从当前正在运行的内核直接引导到一个新内核.Hoho,不懂了吧?不懂就不懂吧,这个我也没法让您懂,网上有关于kexec的介绍,不过要对kexec有一个直观的了解的话可以做一次kdump的实验,网上也有文档,RHEL4/5发行版里边都包含有相关的rpm包,没记错的话,Redhat官方网站上有介绍如何在他们家的系统上配置kdump的文档.经常调试内核的兄弟们应该会比较清楚.不懂也没有关系,总之,你只需要知道,自从有了kexec,你的系统里就不再是一个内核了,它可以有两个,启动的时候,先启动一个小的,然后小的负责加载大的,当大了挂了之后小的可以让它快速的重起,也就是说,这样做的好处就是在调试这个大的内核的时候,当这个大的内核崩溃了之后小的就可以让它重起.)而一旦硬件们进入了那个不三不四的状态之后,等到snapshot被加载了之后,resume会执行,resume并不知道这个状态是个不三不四的状态,它以为这个状态就是正常的那种suspend的状态或者是reset之后的状态,这样子就会出现问题,从而导致设备没法工作.因此需要对resume的设备执行一次reset,而这个宏就为了支持这个.具体来说,比如我们可以在EHCI主机控制器的驱动程序中看到如下的代码,来自drivers/usb/host/ehci-pci.c:
257 /* make sure snapshot being resumed re-enumerates everything */
258 if (message.event == PM_EVENT_PRETHAW) {
259 ehci_halt(ehci);
260 ehci_reset(ehci);
261 }
同样在UHCI或者OHCI驱动中也能看到类似的代码,比如OHCI的,drivers/usb/host/ohci-pci.c:
229 /* make sure snapshot being resumed re-enumerates everything */
230 if (message.event == PM_EVENT_PRETHAW)
231 ohci_usb_reset(ohci);
比如UHCI的,drivers/usb/host/uhci-hcd.c:
768 /* make sure snapshot being resumed re-enumerates everything */
769 if (message.event == PM_EVENT_PRETHAW)
770 uhci_hc_died(uhci);
我们如果到代码中具体去看,会发现这些代码来自相应驱动中的suspend()函数的最后,即无论如何把这些设备给我reset一次,以清除在小内核上执行suspend()留下的恶果,从而保证resume函数能够正确的工作.之所以专门拿主机控制器驱动程序来举例子说,是因为当前内核中,USB子系统里会使用到PRETHAW的只有Host controller driver,因为只有它们有这样一个问题.
另外,关于SUSPEND,FREEZE,它们都属于suspended状态的一种,而设备只可能由ON进入这两种状态之一或者由这两种状态之一进入ON,即比如,设备可以从ON进入FREEZE,也可以从ON进入SUSPEND,但设备绝不可以从FREEZE进入SUSPEND,也不可以从SUSPEND进入FREEZE.
当然 , 说这么细 , 估计您早晕了 , 别慌 , 其实我也晕了 . 不过没关系 , 因为实际上你看到最多的还是 PM_EVENT_ON 和 PM_EVENT_SUSPEND, 另外两个宏您在整个 usb 子系统也难得遇上几次 . 至少在咱们的 Hub 相关的这个故事中 , 您不会遇见另外那两个宏被使用 .更多推荐
所有评论(0)