SoFunction
Updated on 2025-04-07

Android Kernel Code Wake_up Source Code Analysis

Common usage in the kernel:

The kernel has a function wake_up and wake_up_interruptible. Usually, seeing these two function calls is to wake up the thread on the waiting queue.

Until I looked at the source code of epoll, I found that this was not the case.

    bool wakeup_condition;
    wait_queue_head_t wait_queue;
    init_waitqueue_head(&wait_queue);
    wait_queue_entry_t wq_entry
// wait 
    wait_event_interruptible(&wait_queue, wakeup_condition || kthread_should_stop());
// Wake up// Set the wait condition to true and wake up    wakeup_condition = true;
    wake_up(&wait_queue);

Wake_up source code:

// common/include/linux/
#define TASK_NORMAL			(TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE)
#define wake_up(x)			        __wake_up(x, TASK_NORMAL, 1, NULL)
#define wake_up_interruptible(x)	__wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)
// common/kernel/sched/
// wake_up is a macro, and after expansion, the __wake_up function is called// __wake_up(x, TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE, 1, NULL)
int __wake_up(struct wait_queue_head *wq_head, unsigned int mode, int nr_exclusive, void *key)
{
	return __wake_up_common_lock(wq_head, mode, nr_exclusive, 0, key);
}
EXPORT_SYMBOL(__wake_up);
// __wake_up_common_lock(wq_head, TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE, 1, 0, NULL)
static int __wake_up_common_lock(struct wait_queue_head *wq_head, unsigned int mode,
			int nr_exclusive, int wake_flags, void *key)
{
	unsigned long flags;
	wait_queue_entry_t bookmark;
	int remaining = nr_exclusive;
	 = 0;
	 = NULL;
	 = NULL;
	INIT_LIST_HEAD(&);//Initialize the linked list: The next and prev pointers of the linked list point to the linked list's own address	do {
		spin_lock_irqsave(&wq_head->lock, flags);//Spin lock lock, lock the queue		remaining = __wake_up_common(wq_head, mode, remaining, wake_flags, key, &bookmark);
		spin_unlock_irqrestore(&wq_head->lock, flags);//Spin lock unlock	} while ( & WQ_FLAG_BOOKMARK);
	return nr_exclusive - remaining;//When the queue is empty, remaining=nr_exclusive, at this time return 0;}
// __wake_up_common(wq_head, TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE, 1, 0, NULL, &bookmark);
static int __wake_up_common(struct wait_queue_head *wq_head, unsigned int mode,
			int nr_exclusive, int wake_flags, void *key,
			wait_queue_entry_t *bookmark)
{
	wait_queue_entry_t *curr, *next;
	int cnt = 0;
	lockdep_assert_held(&wq_head->lock);
    //  = 0;  WQ_FLAG_BOOKMARK = 0x04;
	if (bookmark && (bookmark->flags & WQ_FLAG_BOOKMARK)) {//This branch will not be entered		curr = list_next_entry(bookmark, entry);
		list_del(&bookmark->entry);
		bookmark->flags = 0;
	} else
		curr = list_first_entry(&wq_head->head, wait_queue_entry_t, entry);//Get the first element of the wq_head queue	if (&curr->entry == &wq_head->head)// When the queue is empty, the passed nr_exclusive will be directly returned to the passed nr_exclusive		return nr_exclusive;
	list_for_each_entry_safe_from(curr, next, &wq_head->head, entry) {//Transfer the link table		unsigned flags = curr->flags;
		int ret;
		if (flags & WQ_FLAG_BOOKMARK)
			continue;
/*
 Calling the callback function func in wait_queue_entry_t
 // Different results will appear here depending on the type of func.
 Use init_waitqueue_entry initialized wait_queue_entry, func = default_wake_function, this function will wake up the thread on curr->private.
 The wait_queue_entry_t initialized with init_waitqueue_func_entry is just to make ordinary function calls.
 */
		ret = curr->func(curr, mode, wake_flags, key);
		if (ret < 0)
			break;
		if (ret && (flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive)
			break;
		if (bookmark && (++cnt > WAITQUEUE_WALK_BREAK_CNT) &&
				(&next->entry != &wq_head->head)) {
			bookmark->flags = WQ_FLAG_BOOKMARK;
			list_add_tail(&bookmark->entry, &next->entry);
			break;
		}
	}
	return nr_exclusive;
}

func assignment process

wait_queue_head and wait_queue_entry data structures

//Kernel 4.14 after// common/include/linux/
struct wait_queue_head {// wait queue	spinlock_t		lock;     // Spin lock	struct list_head	head; // When adding to the wait queue, wait_queue_entry.entry is added to this head link table};
/*
 * A single wait-queue entry structure:
 */
struct wait_queue_entry {// An item in the wait queue	unsigned int		flags;
	void			*private;   // Private data, representing threads in init_waitqueue_entry, and null in init_waitqueue_func_entry	wait_queue_func_t	func;   // Callback function	struct list_head	entry;  // When adding to the wait queue, this entry is added to the link table of wait_queue_head.head};
typedef struct wait_queue_head wait_queue_head_t;   // wait_queue_head_t same as wait_queue_headtypedef struct wait_queue_entry wait_queue_entry_t; // wait_queue_entry_t same as wait_queue_entry

forwait_queue_entryThere are two commonly used initialization methodsinit_waitqueue_entryandinit_waitqueue_func_entry

Two waiting tasks: wait_queue_entry: thread and function

// common/include/linux/
static inline void init_waitqueue_entry(struct wait_queue_entry *wq_entry, struct task_struct *p)
{
	wq_entry->flags		= 0;
	wq_entry->private	= p; //Storage threads that need to be woken into private data    // func assigns the default_wake_function function    // The function of this function is to wake up the thread on the waiting queue	wq_entry->func		= default_wake_function; // This function is: wake up the thread p}
static inline void init_waitqueue_func_entry(struct wait_queue_entry *wq_entry, wait_queue_func_t func)
{
	wq_entry->flags		= 0;
	wq_entry->private	= NULL;
	wq_entry->func		= func; // Assign the incoming callback function directly to wq_entry->func}

default_wake_function function

The function of this function is basically equivalent towake_up_processfunction.

int default_wake_function(wait_queue_entry_t *curr, unsigned mode, int wake_flags,
			  void *key)
{
	WARN_ON_ONCE(IS_ENABLED(CONFIG_SCHED_DEBUG) && wake_flags & ~WF_SYNC);
    //The try_to_wake_up function achieves the purpose of waking up a sleepy and stopping process by setting the process state to TASK_RUNNING and inserting the process into the local CPU running queue rq.    // curr->private stores threads that need to be woken up	return try_to_wake_up(curr->private, mode, wake_flags);
}
EXPORT_SYMBOL(default_wake_function);

To sum up:

  • wake_up may be to wake up the thread on the queue, or it may just trigger a callback.

Two uses of wake_up:

    bool wakeup_condition;
    wait_queue_head_t wait_queue;
    init_waitqueue_head(&wait_queue);
    wait_queue_entry_t wq_entry
// wait
The first usage:Thread waiting
    wait_event_interruptible(&wait_queue, wakeup_condition || kthread_should_stop());
The second usage:Add a callback to the waiting queue
    init_waitqueue_func_entry(&wq_entry, callback);
    add_wait_queue(&wait_queue, &wq_entry);
// Wake up  Set the waiting condition totrue,And wake up
    wakeup_condition = true;
// traverse the queue internally and call each wait_queue_entry func function, which produces different effects according to the func    wake_up(&wait_queue);

Note: Based on the analysis of Kernel 4.14 later versions, please pay attention to my other related articles about the Android kernel code wake_up!