linux user and kerne thread suspend 实质

linux4.9  aarch32

linux hotboot的流程会先freeze user task 然后在freeze kernel task,user task的 的freeze状态就是移除运行队列,kernel task的freeze的状态分为两种,一种是手动处理里freeze信号的task一种是不处理freeze信号的task

enter_state()
-->suspend_prepare(state);
-->suspend_freeze_processes();

static inline int suspend_freeze_processes(void)
{
	int error;

	error = freeze_processes();
	/*
	 * 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)
		thaw_processes();

	return error;
}

freeze_process 是freeze userspace 的thread,freeze_kernel_threads是用来freeze kernel space 的threadpm_freezing 全局变量标志userspace 开始进入freezing的阶段,先将current的进程设置为suspend_task,该task负责将所有的其他userspace task freeze,调用try_to_freeze_tasks,在该函数中suspend_task会一直在while sleep中,调用freeze_task函数对request task 进入suspend,退出的条件有如下两个,一旦退出将调用thaw_process将freeze的进程重新解冻
1 (!todo || time_after(jiffies, end_time)),freeze_timeout_msecs = 20 * MSEC_PER_SEC,20s的时间内没有完成freeze动作则退出

2 (pm_wakeup_pending())系统当前有wakup event要处理,saved_count是保存的在开始进入try_to_susped的时刻的系统当前的注册的wakeup_event数量,如果在suspend的过程中又注册了wakeup_event的也会阻止userspace task进入suspend,主要代码是split_counters(&cnt, &inpr); ret = (cnt != saved_count || inpr > 0);例如auto_sleep与wake_lock机制中的__pm_stay_awake-->wakeup_source_report_event-->wakeup_source_active-->inc combined_event_count

/*
 * Combined counters of registered wakeup events and wakeup events in progress.
 * They need to be modified together atomically, so it's better to use one
 * atomic variable to hold them both.
 */
static atomic_t combined_event_count = ATOMIC_INIT(0);


freeze_task函数中会对user space 的thread 发送信号,这样当suspend_task 让出cpu的时候,其他的thread 从kernel space返回时发现有信号需要处理,do_signal-->get_signal-->try_to_freeze-->__refrigerator,冰冻的状态就是将task的状态设置成task_uninterruptable 和frozen然后schedule让出cpu,因此suspend_task不停的努力工作下,最终todo为0所有的user task 变成了refrigerator的状态

int freeze_processes(void)
{

	/* Make sure this task doesn't get frozen */
	current->flags |= PF_SUSPEND_TASK;

	pr_info("Freezing user space processes ... ");
	pm_freezing = true;
	error = try_to_freeze_tasks(true);
	/*
	 * Now that the whole userspace is frozen we need to disbale
	 * the OOM killer to disallow any further interference with
	 * killable tasks. There is no guarantee oom victims will
	 * ever reach a point they go away we have to wait with a timeout.
	 */
	if (error)
		thaw_processes();
	return error;
}

kernel space的task的freeze同样是调用的try_to_freeze_tasks函数,只是传递的参数为false,参数为false的时候会去处理workqueue对应代码if (!user_only) {wq_busy = freeze_workqueues_busy(); todo += wq_busy;} 要等到工作队列也完成freeze才认为所有的task 完成freeze。

对task发送suspend请求的函数freeze_task,也能从中看出针对kernel task的不同处理if (!(p->flags & PF_KTHREAD)) fake_signal_wake_up(p); else wake_up_state(p, TASK_INTERRUPTIBLE); userspace的task是通过信号的机制发送suspend request的,kernelspace的task是调用wakeup_state将当前处于interruptible的task唤醒放到运行队列中。

如果该task在等待信号量或者在主动休眠中比如down的实现是把task设置成task_uninterruptible的状态msleep也是同样设置为task_uninterruptible状态,这种task既然已经处于task_uninterruptible 状态,系统就不会再额外处理它

kernelspace的task 在创建的时候默认都是PF_NOFREEZE,不会被suspend_task处理,如果需要的话需要手动调用函数

set_freezable();

while(1){try_to_freeze(); ......... sleep();}

set_freezeable函数current->flags &= ~PF_NOFREEZE;,wake_up_states唤醒task后kernelspace task也就会进入refrigerator状态

 

/**
 * freeze_kernel_threads - Make freezable kernel threads go to the refrigerator.
 *
 * On success, returns 0.  On failure, -errno and only the kernel threads are
 * thawed, so as to give a chance to the caller to do additional cleanups
 * (if any) before thawing the userspace tasks. So, it is the responsibility
 * of the caller to thaw the userspace tasks, when the time is right.
 */
int freeze_kernel_threads(void)
{
	pr_info("Freezing remaining freezable tasks ... ");
	pm_nosig_freezing = true;
	error = try_to_freeze_tasks(false);
	if (error)
		thaw_kernel_threads();
	return error;
}

 

static int try_to_freeze_tasks(bool user_only)
{
	while (true) {
		todo = 0;
		for_each_process_thread(g, p) {
			if (p == current || !freeze_task(p))
				continue;
			if (!freezer_should_skip(p))
				todo++;
		}
		if (!user_only) {
			wq_busy = freeze_workqueues_busy();
			todo += wq_busy;
		}
		if (!todo || time_after(jiffies, end_time))
			break;

		if (pm_wakeup_pending()) {
			wakeup = true;
			break;
		}
		/*
		 * We need to retry, but first give the freezing tasks some
		 * time to enter the refrigerator.  Start with an initial
		 * 1 ms sleep followed by exponential backoff until 8 ms.
		 */
		usleep_range(sleep_usecs / 2, sleep_usecs);
		if (sleep_usecs < 8 * USEC_PER_MSEC)
			sleep_usecs *= 2;
	}
	if (todo) {
		pr_cont("\n");
		pr_err("Freezing of tasks %s after %d.%03d seconds "
		       "(%d tasks refusing to freeze, wq_busy=%d):\n",
		       wakeup ? "aborted" : "failed",
		       elapsed_msecs / 1000, elapsed_msecs % 1000,
		       todo - wq_busy, wq_busy);

		if (wq_busy)
			show_workqueue_state();

		if (!wakeup) {
			for_each_process_thread(g, p) {
				if (p != current && !freezer_should_skip(p)
				    && freezing(p) && !frozen(p))
					sched_show_task(p);
			}
		}
	} else {
		pr_cont("(elapsed %d.%03d seconds) ", elapsed_msecs / 1000,
			elapsed_msecs % 1000);
	}

	return todo ? -EBUSY : 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shenhuxi_yu

感谢投币,继续输出

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值