system suspend/resume过程中的异步操作pm_async

在使用sleepgraph分析并优化suspend/resume flow一节https://blog.csdn.net/dachai/article/details/103785380,我们看到,把scsi_scan_type从”sync”改成”async”之后,resume latency大大降低,能够有效提升suspend/resume效率。

其实,除了这个,还有一个global的pm_async参数,决定suspend/resume过程中,device的suspend/resume是同步还是异步操作。

话不多说,我们先用sleepgraph看一下pm_async分别为1/0时,suspend/resume的情况如何。

pm_async=1时,sleepgraph获取STR suspend/resume流程如下:

pm_async=0时,sleepgraph获取STR suspend/resume流程如下:

通过对以上两张图片对比分析,可以得出以下结论:

1)pm_async=1时,支持async_suspend的device执行异步操作,每个阶段的时长由耗时最长的那个device来决定;

在suspend/suspend_late/suspend_noirq以及resume_noirq/resume_early/resume阶段,不同device的操作是可以同时进行的。

2)pm_async=0时,所有device的操作都需要串行执行,每个阶段的时长都是所有device操作时长的总和;

3)使用async PM的话,对kernel resume的改善较为明显,kernel resume时长从680ms降低到319ms。

4)在kernel suspend过程中,设置pm_async与否,影响不太大。这是因为scsi disk suspend过程耗时过长,其他device的suspend时长跟它都不在一个数量级上,即使设置了async suspend,suspend 阶段的耗时最终还是由最大的sd suspend时长来决定。

那么pm_async到底是如何影响suspend/resume流程的呢?而且,众所周知,Linux device model中,很多device可能存在parent-child或provider-consumer的关系,在suspend时,需要保证所有的children/consumer device都完成suspend操作之后,才去执行parent/provider device的suspend操作;而在resume的时候,又需要先resume parent/provider device的function,之后才能resume children/consumer device,而如果是async异步操作的话,是如何保证这一点的呢?我们通过source code去了解一下。

通过/sys/power/pm_async来切换sync和async

1、异步执行device的suspend/resume

echo 1 > /sys/power/pm_async  使用async异步的方式执行suspend/resume流程

我们以dpm_suspend和dpm_resume为例进行说明

echo 1 > /sys/power/pm_async
pm_async_store
{
    pm_async_enabled = 1;
}

dpm_suspend(state)
{
    ……
    while(!list_empty(&dpm_prepared_list))    
    {
        //1.遍历dpm_prepare_list中的每个device,执行device_suspend()函数
        struct device *dev = to_device(dpm_prepared_list.prev);
        error = device_suspend(dev);
        {
            /*
             * 1.1 如果使能了async,把async_suspend加入到workqueue中异步执行之后,
             * 就可以返回了
             */
            ret = dpm_async_fn(dev, async_suspend);    
            {
                ……
                //1.1.1 把async_suspend function加入到async schedule异步执行                            
                async_schedule(async_suspend, dev);	
                {
                    async_schedule_node(func, data, NUMA_NO_NODE);
                    {
                        async_schedule_node_domain(func, data, node, &async_dfl_domain);
                        {
                            //1) 创建一个async_entry
                            struct async_entry *entry;
                            entry = kzalloc(sizeof(struct async_entry), GFP_ATOMIC);
                            //2) 初始化async_entry并加入到domain中;
                            INIT_LIST_HEAD(&entry->domain_list);
                            INIT_LIST_HEAD(&entry->global_list);
                            INIT_WORK(&entry->work, async_run_entry_fn);
                            entry->func = func;
                            entry->data = data;
                            entry->domain = domain;
                            
                            list_add_tail(&entry->domain_list, &domain->pending);
                            if(domain->registered)
                                list_add_tail(&entry->global_list,&async_global_pending);
                            //3) 将entry->work加入到workqueue中等待调度执
                            atomic_inc(&entry_count);
                            current->flags |= PF_USED_ASYNC;
                            queue_work_node(node, system_unbound_wq, &entry->work);						
                        }
                     }
                }
            }
            if(ret)
                return 0;
            /* 
             * 1.2 如果不支持async操作,或async操作失败,
             * 就需要同步执行该device的suspend操作之后,才返回
             */
            return __device_suspend(dev, pm_transition, false);
        }
    }
    //2. 等待所有device异步的suspend操作完成
    //这也是我们在sleepgraph图形上看到的suspend phase的时长由耗时最长的device决定
    async_synchronize_full();		
    {
        async_synchronize_full_domain(NULL);
        {
            async_synchronize_cookie_domain(ASYNC_COOKIE_MAX, domain);
            {
                ……
                //等待async_done信号,并满足lowest_in_progress(domain)超出cookie
                wait_event(async_done, lowest_in_progress(domain) >= cookie);
            }
        }
    }
}

async work被调度时,执行async_run_entry_fn函数
async_run_entry_fn (struct work *work)
{
    struct async_entry *entry = container_of(work, struct async_entry, work);
    //1.执行async_entry对应的function;
    entry->func(entry->data, entry->cookie);
    即async_suspend()
    {
        __device_suspend(dev, pm_transition, true)
        {
            / * 
              * 等待children或consumer device的suspend操作完成;
              * 这部分async操作特有的,虽然所有device 的suspend操作都是依次启动的,
              * 但是其完成时间无法保证,这里需要先check一下
              */ 
            dpm_wait_for_subordinate(dev, async);
            {
                //等待该device的所有children device的suspend操作完
                dpm_wait_for_children(dev,async);
                {
                    device_for_each_child(dev, &async, dpm_wait_fn);
                    {
                        ……
                        //对该device的每个child device,执行dpm_wait_fn
                        klist_iter_init(&parent->p->klist_children, &i);
                        while(!error && (child==next_device(&i)))
                        {
                            error = dpm_wait_fn(child, data);
                            {
                                dpm_wait(child, data);
                                {
                                    wait_for_completion(&dev->power.completion);
                                }
                            }
                        }
                        klist_iter_exit(&i);
                    }
                }
                //等待该device的所有从summer device的suspend操作完成
                dpm_wait_for_consumers(dev, async);
                {
                    list_for_each_entry_rcu(link, &dev->links.consumers, s_node)
                        if (READ_ONCE(link->status) != DL_STATE_DORMANT)
                            dpm_wait(link->consumer, async);
                }
            }
            //2.执行该device的suspend callback;
            //依次从pm_domain/type/class/bus/driver->pm field中获取suspend callback
            dpm_run_callback(callback, dev, state, info);
            
            //3.通知该device的suspend已完成
            complete_all(&dev->power.completion);
        }
    }
    //2.suspend操作完成后,将该entry从pending list中删除
    list_del_init(&entry->domain_list);
    list_del_init(&entry->global_list);

    //3.销毁entry对象
    free(entry);
    atomic_dec(&entry_count);
    
    //4.唤醒等待该device suspend完成的waiter
    wakeup(&async_done);
}    

2、同步执行device的suspend/resume

echo 0 > /sys/power/pm_async  使用sync同步的方式执行suspend/resume流程

跟async异步方式相比,同步是需要等到上一个device的suspend/resume流程结束返回后,才能去执行下一个device的suspend/resume操作。因此,如上图sync的sleepgraph结果所示,suspend phase时长是所有device suspend时长之和。

dpm_suspend(state)
{
    ……
    while(!list_empty(&dpm_prepared_list))    
    {
        //1.遍历dpm_prepare_list中的每个device,执行device_suspend()函数
        struct device *dev = to_device(dpm_prepared_list.prev);
        error = device_suspend(dev);
        {
             /* 
             * 如果不支持async操作,就需要同步执行该device的suspend操作之后,才返回
             */
            return __device_suspend(dev, pm_transition, false);
            /*
             * __device_suspend()跟异步async_entry worker handler相同,
             * 都是先完成device的suspend操作,再complete_all(&dev->power.completion);
             */
        }
    }
    //2. 等待所有device的suspend操作完成,
    //这里只是为了跟async保持一致,实际上device_suspend()会等待每个device的suspend操作完成
    async_synchronize_full();		
    {
        async_synchronize_full_domain(NULL);
        {
            async_synchronize_cookie_domain(ASYNC_COOKIE_MAX, domain);
            {
                ……
                //等待async_done信号,并满足lowest_in_progress(domain)超出cookie
                wait_event(async_done, lowest_in_progress(domain) >= cookie);
            }
        }
    }
}

简单总结一下,支持pm_async的话,

1)在suspend/suspend_noirq/suspend_late、resume_early/resume_noirq/resume阶段,可以异步进行device的suspend/resume的操作;

2)一个阶段结束之前,需要等待所有async的device操作完成(每个phase需要同步一下)。

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值