蜗窝科技

kernel reboot -> device_shutdown(void) 函数相关疑惑

蜗窝讨论区存档 · Linux kernel技术问答 · 楼主 victor.guo · 2016-09-19 · 10 帖

本文是原「蜗窝讨论区」的历史存档(2016-09-19),来自版块「Linux kernel技术问答」,共 10 帖。讨论区已停止服务,此处仅供查阅。

victor.guo · 2016-09-19 15:43

大家好,初次发帖,请多多支持。

项目过程中遇到一个很棘手的问题,详细如下: 手机进行自动开关机压力测试(高通8937)400-1000次左右大概会有一次卡住导致不关机(并没有kernel panic),现象就是卡在关机动画,adb还可以连,uart log也正常输出,我的做法就是把kernel 关机流程全部加上printk,然后弄10台机器进行压力测试,等到出现问题后,触发ramdump,再解开ramdump,查看kmsg中加的trace走到哪一步进一步分析卡在哪里。

但是这个情况很特殊!!!!加了kernel log后,浮现概率大大降低,就算进行2000次测试也不一定可以出现问题(进一步怀疑是某一个dev时序问题卡住,但是不知道凶手是谁..),好不容易抓到一次,大致定位到了出问题就是卡在函数就是 device_shutdown函数,可惜log太少没有找出卡在具体哪一个device,现在还在继续压力测试中,我们分析极有可能是卡在pm_runtime_barrier 函数中,但是这个内核函数有点复杂,不太懂这块。 希望有高手可以指定一二,重点详细的帮忙分析下device_shutdown 整个函数,多谢!!!!! 集思广益,希望大神们不吝给出各种调试分析手段!

void device_shutdown(void) { struct device dev, parent;

     spin_lock(&devices_kset->list_lock);
     /*
     * Walk the devices list backward, shutting down each in turn.
     * Beware that device unplug events may also start pulling
     * devices offline, even as the system is shutting down.
     */
     while (!list_empty(&devices_kset->list)) {
               dev = list_entry(devices_kset->list.prev, struct device,
                                 kobj.entry);

** dev_p = (char )dev->kobj.name; step1++;*

               /*
               * hold reference count of device's parent to
               * prevent it from being freed because parent's
               * lock is to be held
               */
               parent = get_device(dev->parent);
               get_device(dev);
               /*
               * Make sure the device is off the kset list, in the
               * event that dev->*->shutdown( doesn't remove it.
               */
               list_del_init(&dev->kobj.entry);
               spin_unlock(&devices_kset->list_lock);
           /* hold lock to avoid race with probe/release */
           if (parent)
                    device_lock(parent);
           device_lock(dev);

           /* Don't allow any more runtime suspends */
           pm_runtime_get_noresume(dev);
           pm_runtime_barrier(dev);

          ** step2++;**

           if (dev->bus && dev->bus->shutdown) {
                    if (initcall_debug)
                             dev_info(dev, "shutdown\n");
                    dev->bus->shutdown(dev);
           } else if (dev->driver && dev->driver->shutdown) {
                    if (initcall_debug)
                             dev_info(dev, "shutdown\n");
                    dev->driver->shutdown(dev);
           }

           device_unlock(dev);
           if (parent)
                    device_unlock(parent);

           put_device(dev);
           put_device(parent);

           spin_lock(&devices_kset->list_lock);
 }
 spin_unlock(&devices_kset->list_lock);

}

上面的 dev_p = (char *)dev->kobj.name; 和step1,step2是我加的计数器,用于记录出问题时候跑了哪一步,是哪个dev卡住,可惜目前还没出结果,只能从代码层级深入挖掘o(╯□╰)o

wowo · 2016-09-19 16:03

adb能用,说明负责poweroff的进程被调度走了。 如果真觉得pm_runtime_barrier的嫌疑比较大,那就查一下这个接口:


[== Undefined ==]
pm_runtime_barrier-->__pm_runtime_barrier
...
        if (dev->power.runtime_status == RPM_SUSPENDING                         
            || dev->power.runtime_status == RPM_RESUMING                        
            || dev->power.idle_notification) {                                  
                DEFINE_WAIT(wait);                                              
            /* Suspend, wake-up or idle notification in progress. */        
            for (;;) {                                                      
                    prepare_to_wait(&dev->power.wait_queue, &wait,          
                                    TASK_UNINTERRUPTIBLE);                  
                    if (dev->power.runtime_status != RPM_SUSPENDING         
                        && dev->power.runtime_status != RPM_RESUMING        
                        && !dev->power.idle_notification)                   
                            break;                                          
                    spin_unlock_irq(&dev->power.lock);                      

                    schedule(;                                             

                    spin_lock_irq(&dev->power.lock);                        
            }                                                               
            finish_wait(&dev->power.wait_queue, &wait);                     
    } 

…

首先确认你所使用的系统runtime pm是否使能了(如果没有,就不会是这里)。 如果使能了,有可能在系统poweroff的时候,某个设备正在进入suspend或者resume状态,这时pm_runtime_barrier就会等这个过程结束。如果由于某些原因,无法结束,就导致负责poweroff的进程无法调度回来。

试试在这个里面加一些调试信息。

victor.guo · 2016-09-20 10:08

wowo 写道: adb能用,说明负责poweroff的进程被调度走了。 如果真觉得pm_runtime_barrier的嫌疑比较大,那就查一下这个接口:


[== Undefined ==]
pm_runtime_barrier-->__pm_runtime_barrier
...
        if (dev->power.runtime_status == RPM_SUSPENDING                         
            || dev->power.runtime_status == RPM_RESUMING                        
            || dev->power.idle_notification) {                                  
                DEFINE_WAIT(wait);                                              
            /* Suspend, wake-up or idle notification in progress. */        
            for (;;) {                                                      
                    prepare_to_wait(&dev->power.wait_queue, &wait,          
                                    TASK_UNINTERRUPTIBLE);                  
                    if (dev->power.runtime_status != RPM_SUSPENDING         
                        && dev->power.runtime_status != RPM_RESUMING        
                        && !dev->power.idle_notification)                   
                            break;                                          
                    spin_unlock_irq(&dev->power.lock);                      

                    schedule(;                                             

                    spin_lock_irq(&dev->power.lock);                        
            }                                                               
            finish_wait(&dev->power.wait_queue, &wait);                     
    } 

…

首先确认你所使用的系统runtime pm是否使能了(如果没有,就不会是这里)。 如果使能了,有可能在系统poweroff的时候,某个设备正在进入suspend或者resume状态,这时pm_runtime_barrier就会等这个过程结束。如果由于某些原因,无法结束,就导致负责poweroff的进程无法调度回来。

试试在这个里面加一些调试信息。


好的,多谢热情回复,我加log在pm_runtime_barrier里面,试试正常情况下有没有跑这里。 另外,昨晚跑了一夜,浮现了一次,dev_p = (char *)dev->kobj.name; 用adb拉出来了,发现卡住的dev是 :bam_dmux_ch_0.2 , 但是不了解这个东西是干什么用的, 正在查代码,想问下您是否了解这个东西?

wowo · 2016-09-20 13:05

看着像高通的东西,我也不了解是什么。

victor.guo · 2016-09-20 22:25

wowo 写道: 看着像高通的东西,我也不了解是什么。

确实是高通的东西,查了下好像是跟battery以及rmnet相关的,不太懂... 另外,下午我在pm_runtime_barrier里面加了log打印,确实是会跑的。 目前的有一种可以交差的解决方案就是在device_shutdown的循环里面加一个mdelay(1);貌似就可以解决问题 ,怀疑是某个dev卸载的时序上问题, 但是没有找到根本原因,挫败感啊....

想问下你知道adb是在关机什么时候给disable掉的呢?出问题的时候adb还可以用,说明这个设备还未被卸载,想从这个上面倒推下. :cool:

wowo · 2016-09-21 09:11

victor.guo 写道:

wowo 写道: 看着像高通的东西,我也不了解是什么。

确实是高通的东西,查了下好像是跟battery以及rmnet相关的,不太懂... 另外,下午我在pm_runtime_barrier里面加了log打印,确实是会跑的。 目前的有一种可以交差的解决方案就是在device_shutdown的循环里面加一个mdelay(1);貌似就可以解决问题 ,怀疑是某个dev卸载的时序上问题, 但是没有找到根本原因,挫败感啊....

想问下你知道adb是在关机什么时候给disable掉的呢?出问题的时候adb还可以用,说明这个设备还未被卸载,想从这个上面倒推下. :cool:

delay 1ms不是一个好方法……

如果想正向的跟,可以写一个脚本,在出问题之后,把所有device在sysfs暴露出来的power状态,打印出来: /sys/devices/.../power # ls autosuspend_delay_ms control runtime_active_time runtime_status runtime_suspended_time 特别是runtime status,如果是我们怀疑的这个场景,应该能够看出来。

victor.guo · 2016-09-21 12:40

wowo 写道:

victor.guo 写道:

wowo 写道: 看着像高通的东西,我也不了解是什么。

确实是高通的东西,查了下好像是跟battery以及rmnet相关的,不太懂... 另外,下午我在pm_runtime_barrier里面加了log打印,确实是会跑的。 目前的有一种可以交差的解决方案就是在device_shutdown的循环里面加一个mdelay(1);貌似就可以解决问题 ,怀疑是某个dev卸载的时序上问题, 但是没有找到根本原因,挫败感啊....

想问下你知道adb是在关机什么时候给disable掉的呢?出问题的时候adb还可以用,说明这个设备还未被卸载,想从这个上面倒推下. :cool:

delay 1ms不是一个好方法……

如果想正向的跟,可以写一个脚本,在出问题之后,把所有device在sysfs暴露出来的power状态,打印出来: /sys/devices/.../power # ls autosuspend_delay_ms control runtime_active_time runtime_status runtime_suspended_time 特别是runtime status,如果是我们怀疑的这个场景,应该能够看出来。

这个要怎么打印?【尴尬】 shell@Life_One_X2:/sys/devices/platform/power $ ls -al -rw-r--r-- root root 4096 2016-01-03 07:38 autosuspend_delay_ms -rw-r--r-- root root 4096 2016-01-03 07:38 control -r--r--r-- root root 4096 2016-01-03 07:38 runtime_active_time -r--r--r-- root root 4096 2016-01-03 07:38 runtime_status -r--r--r-- root root 4096 2016-01-03 07:38 runtime_suspended_time shell@Life_One_X2:/sys/devices/platform/power $ cat runtime_status unsupported

?

wowo · 2016-09-21 13:17

就是cat啦 :cool: 找一个支持使能了runtime_pm的device。


[== Undefined ==]
drivers/base/power/sysfs.c

static ssize_t rtpm_status_show(struct device *dev, struct device_attribute *attr, char *buf) {
const char *p;

    if (dev->power.runtime_error) {
            p = "error\n";
    } else if (dev->power.disable_depth) {
            p = "unsupported\n";
    } else {
            switch (dev->power.runtime_status) {
            case RPM_SUSPENDED:
                    p = "suspended\n";
                    break;
            case RPM_SUSPENDING:
                    p = "suspending\n";
                    break;
            case RPM_RESUMING:      
                    p = "resuming\n";
                    break; 
            case RPM_ACTIVE:
                    p = "active\n";
                    break;
            default:
                    return -EIO;
            }
    }
    return sprintf(buf, p);

}

victor.guo · 2016-09-22 12:34

shell@Life_One_X2:/sys/devices/platform/power $ cat runtime_status unsupported

这么说我这个默认就是不支持了。

wowo · 2016-09-22 13:36

victor.guo 写道: shell@Life_One_X2:/sys/devices/platform/power $ cat runtime_status unsupported

这么说我这个默认就是不支持了。

你要看某一个具体的设备的吧?不应该是“/sys/devices/platform/power”?