suspend_resume: suspend_enter[1] begin suspend_resume: sync_filesystems[0] begin suspend_resume: sync_filesystems[0] end suspend_resume: freeze_processes[0] begin suspend_resume: freeze_processes[0] end suspend_resume: suspend_enter[1] end suspend_resume: dpm_prepare[2] begin suspend_resume: dpm_prepare[2] end suspend_resume: dpm_suspend[2] begin suspend_resume: dpm_suspend[2] end suspend_resume: dpm_suspend_late[2] begin suspend_resume: dpm_suspend_late[2] end suspend_resume: dpm_suspend_noirq[2] begin suspend_resume: dpm_suspend_noirq[2] end No CPU_OFF… No syscore_suspend… suspend_resume: machine_suspend[1] begin suspend_resume: machine_suspend[1] end No suscore_resume… No CPU_ON… suspend_resume: dpm_resume_noirq[16] begin suspend_resume: dpm_resume_noirq[16] end suspend_resume: dpm_resume_early[16] begin suspend_resume: dpm_resume_early[16] end suspend_resume: dpm_resume[16] begin suspend_resume: dpm_resume[16] end suspend_resume: dpm_complete[16] begin suspend_resume: dpm_complete[16] end suspend_resume: resume_console[1] begin suspend_resume: resume_console[1] end suspend_resume: thaw_processes[0] begin suspend_resume: thaw_processes[0] end
suspend_resume: suspend_enter[3] begin suspend_resume: sync_filesystems[0] begin suspend_resume: sync_filesystems[0] end suspend_resume: freeze_processes[0] begin suspend_resume: freeze_processes[0] end suspend_resume: suspend_enter[3] end suspend_resume: dpm_prepare[2] begin suspend_resume: dpm_prepare[2] end suspend_resume: dpm_suspend[2] begin suspend_resume: dpm_suspend[2] end suspend_resume: dpm_suspend_late[2] begin suspend_resume: dpm_suspend_late[2] end suspend_resume: dpm_suspend_noirq[2] begin suspend_resume: dpm_suspend_noirq[2] end suspend_resume: CPU_OFF[1-7] begin/end suspend_resume: syscore_suspend[0] begin/end suspend_resume: machine_suspend[3] begin suspend_resume: machine_suspend[3] end suspend_resume: syscore_resume[0] begin/end suspend_resume: CPU_ON[1-7] begin/end suspend_resume: dpm_resume_noirq[16] begin suspend_resume: dpm_resume_noirq[16] end suspend_resume: dpm_resume_early[16] begin suspend_resume: dpm_resume_early[16] end suspend_resume: dpm_resume[16] begin suspend_resume: dpm_resume[16] end suspend_resume: dpm_complete[16] begin suspend_resume: dpm_complete[16] end suspend_resume: resume_console[3] begin suspend_resume: resume_console[3] end suspend_resume: thaw_processes[0] begin suspend_resume: thaw_processes[0] end
static inline int suspend_freeze_processes(void) { int error;
error = freeze_processes(); 触发用户空间进程进入freeze状态。当前进程不会被冻结。因为冻结失败的进程会自动被解冻,所以不需要进行错误处理。 /* * freeze_processes() automatically thaws every task if freezing * fails. So we need not do anything extra upon error. */ if (error) return error;
error = freeze_kernel_threads(); 冻结内核线程 /* * freeze_kernel_threads() thaws only kernel threads upon freezing * failure. So we have to thaw the userspace tasks ourselves. */ if (error) 由于freeze_kernel_threads冻结失败,只会解冻内核线程。所以还需要对用户空间进程进行解冻。 thaw_processes();
int suspend_devices_and_enter(suspend_state_t state) { int error; bool wakeup = false;
if (!sleep_state_supported(state)) return -ENOSYS;
error = platform_suspend_begin(state); if (error) goto Close;
suspend_console(); 关闭console子系统,暂停printk打印 suspend_test_start(); error = dpm_suspend_start(PMSG_SUSPEND); suspend_prepare(dpm_prepare)、suspend(dpm_suspend)两阶段 if (error) { pr_err("PM: Some devices failed to suspend, or early wake event detected\n"); log_suspend_abort_reason("Some devices failed to suspend, or early wake event detected"); goto Recover_platform; } suspend_test_finish("suspend devices"); if (suspend_test(TEST_DEVICES)) goto Recover_platform;
do { error = suspend_enter(state, &wakeup); suspend_late(dpm_suspend_late)、suspend_noirq(dpm_suspend_noirq)、suspend_machine、resume_machine、resume_noirq(dpm_resume_noirq)、resume_early(dpm_resume_early) } while (!error && !wakeup && platform_suspend_again(state));
/* Push all the CPUs into the idle loop. */ wake_up_all_idle_cpus(); 强制所有CPU退出idle状态 pr_debug("PM: suspend-to-idle\n"); /* Make the current CPU wait so it can enter the idle loop too. */ wait_event(suspend_freeze_wait_head, suspend_freeze_state == FREEZE_STATE_WAKE); 等待FREEZE_STATE_WAKE事件,进入idle loop pr_debug("PM: resume from suspend-to-idle\n"); !!!!!!!!!!!!!!!!这里即为唤醒之后的执行路径了!!!!!!!!!!!!!!!! cpuidle_pause(); 暂停使用cpuidle put_online_cpus();
/* * Advanced the async threads upfront, * in case the starting of async threads is * delayed by non-async resuming devices. */ list_for_each_entry(dev, &dpm_noirq_list, power.entry) { reinit_completion(&dev->power.completion); if (is_async(dev)) { get_device(dev); async_schedule(async_resume_noirq, dev); } }
while (!list_empty(&dpm_noirq_list)) { 遍历dpm_noirq_list dev = to_device(dpm_noirq_list.next); get_device(dev); list_move_tail(&dev->power.entry, &dpm_late_early_list); 移动设备到下一级dpm_late_early_list mutex_unlock(&dpm_list_mtx);
/* * Advanced the async threads upfront, * in case the starting of async threads is * delayed by non-async resuming devices. */ list_for_each_entry(dev, &dpm_late_early_list, power.entry) { reinit_completion(&dev->power.completion); if (is_async(dev)) { get_device(dev); async_schedule(async_resume_early, dev); } }
while (!list_empty(&dpm_late_early_list)) { dev = to_device(dpm_late_early_list.next); get_device(dev); list_move_tail(&dev->power.entry, &dpm_suspended_list); mutex_unlock(&dpm_list_mtx);
Options: [general] -h Print this help text -v Print the current tool version -verbose Print extra information during execution and analysis -status Test to see if the system is enabled to run this tool -modes List available suspend modes 显示当前支持的suspend模式 -m mode Mode to initiate for suspend ['freeze', 'mem', 'disk'] (default: mem) 设置进入何种模式的suspend -rtcwake t Use rtcwake to autoresume after <t> seconds (default: disabled) 使用rtc来唤醒,参数是间隔时间 [advanced] -f Use ftrace to create device callgraphs (default: disabled) 基于ftrace生成调用关系图 -filter "d1 d2 ..." Filter out all but this list of dev names -x2 Run two suspend/resumes back to back (default: disabled) -x2delay t Minimum millisecond delay <t> between the two test runs (default: 0 ms) -postres t Time after resume completion to wait for post-resume events (default: 0 S) -multi n d Execute <n> consecutive tests at <d> seconds intervals. The outputs will be created in a new subdirectory with a summary page. [utilities] -fpdt Print out the contents of the ACPI Firmware Performance Data Table -usbtopo Print out the current USB topology with power info -usbauto Enable autosuspend for all connected USB devices [android testing] -adb binary Use the given adb binary to run the test on an android device. 参数需要给出adb路径,工具就会对Android设备进行测试,并将结果pull出来。有一点需要注意,在此之前确保adb具有root权限。 The device should already be connected and with root access. Commands will be executed on the device using "adb shell" [re-analyze data from previous runs] 针对之前测试数据重新分析 -ftrace ftracefile Create HTML output using ftrace input -dmesg dmesgfile Create HTML output using dmesg (not needed for kernel >= 3.15) -summary directory Create a summary of all test in this dir
# ----------------- MAIN -------------------- # exec start (skipped if script is loaded as library) if __name__ == '__main__': cmd = '' cmdarg = '' multitest = {'run': False, 'count': 0, 'delay': 0} # loop through the command line arguments args = iter(sys.argv[1:]) for arg in args: …
# just run a utility command and exit if(cmd != ''): if(cmd == 'status'): statusCheck() elif(cmd == 'fpdt'): if(sysvals.android): doError('cannot read FPDT on android device', False) getFPDT(True) elif(cmd == 'usbtopo'): if(sysvals.android): doError('cannot read USB topology '+\ 'on an android device', False) detectUSB(True) elif(cmd == 'modes'): modes = getModes() print modes elif(cmd == 'usbauto'): setUSBDevicesAuto() elif(cmd == 'summary'): print("Generating a summary of folder \"%s\"" % cmdarg) runSummary(cmdarg, True) sys.exit()
# run test on android device if(sysvals.android): 注释此段代码可以在Android上支持callgraph #if(sysvals.usecallgraph): # doError('ftrace (-f) is not yet supported '+\ # 'in the android kernel', False) if(sysvals.notestrun): doError('cannot analyze test files on the '+\ 'android device', False)
# if instructed, re-analyze existing data files if(sysvals.notestrun): 分析已有数据文件,不需要重新测试 rerunTest() sys.exit()
# verify that we can run a test if(not statusCheck()): 检查测试条件是否满足 print('Check FAILED, aborting the test run!') sys.exit()
if multitest['run']: 连续多次测试 # run multiple tests in a separte subdirectory s = 'x%d' % multitest['count'] subdir = datetime.now().strftime('suspend-'+s+'-%m%d%y-%H%M%S') os.mkdir(subdir) for i in range(multitest['count']): if(i != 0): print('Waiting %d seconds...' % (multitest['delay'])) time.sleep(multitest['delay']) print('TEST (%d/%d) START' % (i+1, multitest['count'])) runTest(subdir) 进行单次测试 print('TEST (%d/%d) COMPLETE' % (i+1, multitest['count'])) runSummary(subdir, False) 生成summary.html else: # run the test in the current directory runTest(".")
# prepare for the test if(not sysvals.android): 针对不同的待测设备,初始化ftrace initFtrace() else: initFtraceAndroid() sysvals.initTestOutput(subdir) 生成输出目录,输出文件名等。
vprint('Output files:\n %s' % sysvals.dmesgfile) if(sysvals.usecallgraph or sysvals.usetraceevents or sysvals.usetraceeventsonly): vprint(' %s' % sysvals.ftracefile) vprint(' %s' % sysvals.htmlfile)
# execute the test 执行测试,实际上命令内容基本一致。只是针对Android设备,增加了adb shell '…'。 if(not sysvals.android): executeSuspend() else: executeAndroidSuspend()
# analyze the data and create the html output print('PROCESSING DATA') if(sysvals.usetraceeventsonly): 3.15之后的版本,只需要通过ftrace即可获取足够信息。之前的版本的数据都存在dmesg中。 # data for kernels 3.15 or newer is entirely in ftrace testruns = parseTraceLog() else: # data for kernels older than 3.15 is primarily in dmesg testruns = loadKernelLog() for data in testruns: parseKernelLog(data) if(sysvals.usecallgraph or sysvals.usetraceevents): appendIncompleteTraceLog(testruns) createHTML(testruns) 根据解析的数据生成html矢量图表
# check to see if the display is currently off tp = sysvals.tpath out = os.popen(sysvals.adb+\ ' shell dumpsys power | grep mScreenOn').read().strip() # if so we need to turn it on so we can issue a new suspend if(out.endswith('false')): print('Waking the device up for the test...') # send the KEYPAD_POWER keyevent to wake it up os.system(sysvals.adb+' shell input keyevent 26') # wait a few seconds so the user can see the device wake up time.sleep(3) # execute however many s/r runs requested for count in range(1,sysvals.execcount+1): # clear the kernel ring buffer just as we start os.system(sysvals.adb+' shell dmesg -c > /dev/null 2>&1') 清空dmesg # start ftrace if(sysvals.usetraceevents): print('START TRACING') os.system(sysvals.adb+" shell 'echo 1 > "+tp+"tracing_on'") 开始ftrace抓取 # initiate suspend for count in range(1,sysvals.execcount+1): if(sysvals.usetraceevents): os.system(sysvals.adb+\ " shell 'echo SUSPEND START > "+tp+"trace_marker'") 写SUSPEND START到ftrace,作为开始标记。后面解析log,会以此为标记。 if(sysvals.rtcwake): print('SUSPEND START') print('will autoresume in %d seconds' % sysvals.rtcwaketime) os.system(sysvals.adb+" shell 'echo +%d > /sys/class/rtc/rtc0/wakealarm'"%(sysvals.rtcwaketime)) 设置wakeup resource else: print('SUSPEND START (press a key to resume)')
trace_console_lock("console_unlock start", strlen("console_unlock start"));\ if (console_suspended) { up_console_sem(); return; }
/* * Console drivers are called under logbuf_lock, so * @console_may_schedule should be cleared before; however, we may * end up dumping a lot of lines, for example, if called from * console registration path, and should invoke cond_resched() * between lines if allowable. Not doing so can cause a very long * scheduling stall on a slow console leading to RCU stall and * softlockup warnings which exacerbate the issue with more * messages practically incapacitating the system. */ do_cond_resched = console_may_schedule; console_may_schedule = 0;
/* flush buffered message fragment immediately to console */ console_cont_flush(text, sizeof(text)); again: for (;;) { 如果默认的LOGLEVEL定的比较高,即优先级低,则会有相当多的log需要打印。占用很多时间。 … } console_locked = 0;
/* Release the exclusive_console once it is used */ if (unlikely(exclusive_console)) exclusive_console = NULL;
/* * Someone could have filled up the buffer again, so re-check if there's * something to flush. In case we cannot trylock the console_sem again, * there's a new owner and the console_unlock() from them will do the * flush, no worries. */ raw_spin_lock(&logbuf_lock); retry = console_seq != log_next_seq; raw_spin_unlock_irqrestore(&logbuf_lock, flags);
if (retry && console_trylock()) goto again;
if (wake_klogd) wake_up_klogd(); trace_console_lock("console_unlock end", strlen("console_unlock end"));\ }
/* We show everything that is MORE important than this.. */ #define CONSOLE_LOGLEVEL_SILENT 0 /* Mum's the word */ #define CONSOLE_LOGLEVEL_MIN 1 /* Minimum loglevel we let people use */ #define CONSOLE_LOGLEVEL_QUIET 4 /* Shhh ..., when booted with "quiet" */ #define CONSOLE_LOGLEVEL_DEFAULT 7 /* anything MORE serious than KERN_DEBUG */ #define CONSOLE_LOGLEVEL_DEBUG 10 /* issue debug messages */ #define CONSOLE_LOGLEVEL_MOTORMOUTH 15 /* You can't shut this one up */
7, mem Line 748: resume_complete,resume_console[3],248.54900000002544 Line 748: resume_complete,resume_console[3],248.6340000000382 Line 748: resume_complete,resume_console[3],248.26499999994667 Line 748: resume_complete,resume_console[3],248.3510000000706 Line 748: resume_complete,resume_console[3],248.42499999999745
7, freeze Line 996: resume_complete,resume_console[1],76.18400000001202 Line 996: resume_complete,resume_console[1],76.19500000009793 Line 996: resume_complete,resume_console[1],76.3280000001032 Line 996: resume_complete,resume_console[1],76.1689999999362 Line 996: resume_complete,resume_console[1],76.19999999997162
4, freeze Line 996: resume_complete,resume_console[1],0.1010000000007949 Line 996: resume_complete,resume_console[1],0.10499999999069587 Line 996: resume_complete,resume_console[1],0.09799999997994746 Line 996: resume_complete,resume_console[1],0.1010000000007949 Line 996: resume_complete,resume_console[1],0.10000000003174137
4, mem Line 749: resume_complete,resume_console[3],0.3370000000586515 Line 749: resume_complete,resume_console[3],0.33800000005612674 Line 749: resume_complete,resume_console[3],0.37700000007134804 Line 749: resume_complete,resume_console[3],0.3359999999474894 Line 749: resume_complete,resume_console[3],0.3429999999298161
int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, unsigned int timeout_ms, bool use_busy_signal, bool send_status, bool ignore_crc) { struct mmc_host *host = card->host; int err; struct mmc_command cmd = {0}; unsigned long timeout; u32 status = 0; bool use_r1b_resp = use_busy_signal;
mmc_retune_hold(host);
… /* * We are not allowed to issue a status command and the host * does'nt support MMC_CAP_WAIT_WHILE_BUSY, then we can only * rely on waiting for the stated timeout to be sufficient. */ if (!send_status) { mmc_delay(timeout_ms); goto out; }
/* Requires cpu_add_remove_lock to be held */ static int _cpu_down(unsigned int cpu, int tasks_frozen) { int err, nr_calls = 0; void *hcpu = (void *)(long)cpu; unsigned long mod = tasks_frozen ? CPU_TASKS_FROZEN : 0; struct take_cpu_down_param tcd_param = { .mod = mod, .hcpu = hcpu, };
if (num_online_cpus() == 1) 如果online只有一个CPU,则无法再进行down操作。 return -EBUSY;
if (!cpu_online(cpu)) 如果当前CPU没有online,则无需进行down。 return -EINVAL;
cpu_hotplug_begin(); 取得cpu_hotplug.lock锁
err = __cpu_notify(CPU_DOWN_PREPARE | mod, hcpu, -1, &nr_calls); 在cpu_chain上发从CPU_DOWN_PREPARE状态。 if (err) { nr_calls--; __cpu_notify(CPU_DOWN_FAILED | mod, hcpu, nr_calls, NULL); pr_warn("%s: attempt to take down CPU %u failed\n", __func__, cpu); goto out_release; }
/* * By now we've cleared cpu_active_mask, wait for all preempt-disabled * and RCU users of this state to go away such that all new such users * will observe it. * * For CONFIG_PREEMPT we have preemptible RCU and its sync_rcu() might * not imply sync_sched(), so wait for both. * * Do sync before park smpboot threads to take care the rcu boost case. */ if (IS_ENABLED(CONFIG_PREEMPT)) synchronize_rcu_mult(call_rcu, call_rcu_sched); else synchronize_rcu();
/* * Prevent irq alloc/free while the dying cpu reorganizes the * interrupt affinities. */ irq_lock_sparse();
/* * So now all preempt/rcu users must observe !cpu_active(). */ err = stop_machine(take_cpu_down, &tcd_param, cpumask_of(cpu)); if (err) { /* CPU didn't die: tell everyone. Can't complain. */ cpu_notify_nofail(CPU_DOWN_FAILED | mod, hcpu); irq_unlock_sparse(); goto out_release; } BUG_ON(cpu_online(cpu)); 如果指定的CPU还处于online状态,则触发kernel panic。
/* * The migration_call() CPU_DYING callback will have removed all * runnable tasks from the cpu, there's only the idle task left now * that the migration thread is done doing the stop_machine thing. * * Wait for the stop thread to go away. */ while (!per_cpu(cpu_dead_idle, cpu)) cpu_relax(); smp_mb(); /* Read from cpu_dead_idle before __cpu_die(). */ per_cpu(cpu_dead_idle, cpu) = false;
/* Interrupts are moved away from the dying cpu, reenable alloc/free */ irq_unlock_sparse();
hotplug_cpu__broadcast_tick_pull(cpu); /* This actually kills the CPU. */ __cpu_die(cpu); 调用底层架构相关的cpu_kill回调函数。
/* CPU is completely dead: tell everyone. Too late to complain. */ tick_cleanup_dead_cpu(cpu); cpu_notify_nofail(CPU_DEAD | mod, hcpu);通知完成offline动作的处理器状态为CPU_DEAD。
/* Requires cpu_add_remove_lock to be held */ static int _cpu_up(unsigned int cpu, int tasks_frozen) { int ret, nr_calls = 0; void *hcpu = (void *)(long)cpu; unsigned long mod = tasks_frozen ? CPU_TASKS_FROZEN : 0; struct task_struct *idle;
if (cpu_online(cpu) || !cpu_present(cpu)) { 如果该CPU已经online,则没有必要执行up;或者非present,则无法up。 ret = -EINVAL; goto out; }
idle = idle_thread_get(cpu); 给指定CPU生成一个idle线程 if (IS_ERR(idle)) { ret = PTR_ERR(idle); goto out; }
ret = smpboot_create_threads(cpu); 创建一个用于管理CPU hotplug动作的线程 if (ret) goto out;
ret = __cpu_notify(CPU_UP_PREPARE | mod, hcpu, -1, &nr_calls);通知cpu_chain中的处理器,当前正在online的CPU状态为CPU_UP_PREPARE。 if (ret) { nr_calls--; pr_warn("%s: attempt to bring up CPU %u failed\n", __func__, cpu); goto out_notify; }
/* Arch-specific enabling code. */ ret = __cpu_up(cpu, idle); 调用更底层的使能CPU操作。
if (ret != 0) goto out_notify; BUG_ON(!cpu_online(cpu));
/* Now call notifier in preparation. */ cpu_notify(CPU_ONLINE | mod, hcpu); 通知cpu_chanin中的处理器,目前online动作的处理器的状态为CPU_ONLINE。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)