tyChen
tyChen

Reputation: 1494

Why thread_info should be the first element in task_struct?

When reading Linux kernel source code, I find one thing makes me confused.

In task_struct, it says like this

struct task_struct {
#ifdef CONFIG_THREAD_INFO_IN_TASK
    /*
     * For reasons of header soup (see current_thread_info()), this
     * must be the first element of task_struct.
     */
    struct thread_info      thread_info;
#endif

...
};

The complete code is here, can you tell me why this must be the first element in task_struct?

Upvotes: 4

Views: 2383

Answers (2)

tyChen
tyChen

Reputation: 1494

After searching many books, websites and debugging the Linux kernel, I understand the design of thread_info and I get to know why it must be the first element in task_struct.

Firstly, we need to understand Linux kernel stack.

Then, we come to the core : we need to get to kernel stack by task_struct some time, and we need to get to task_struct by kernel stack some time. So we must have some way to do it.

1. task_struct to kernel stack

It is easy to use stack like below

static inline void *task_stack_page(const struct task_struct *task)
{
  return task->stack;
}

What's more, we can get to pt_regs by task_pt_regs(task)

2.kernel stack to task_struct

Here is the use of thread_info. Let me put two version here

struct thread_info {
  struct task_struct  *task;    /* main task structure */
  __u32      flags;    /* low level flags */
  __u32      status;    /* thread synchronous flags */
  __u32      cpu;    /* current CPU */
  mm_segment_t    addr_limit;
  unsigned int    sig_on_uaccess_error:1;
  unsigned int    uaccess_err:1;  /* uaccess failed */
};


struct thread_info {
  unsigned long flags;          /* low level flags */
  unsigned long status;    /* thread synchronous flags */    
};


The first one is the old version, it includes task_struct, from which we can go straightly to task_struct. But it costs too much to strore it, so the new version delete all.

In the old version, we have this function to get it.

static inline struct thread_info *current_thread_info(void)
{
  return (struct thread_info *)(current_top_of_stack() - THREAD_SIZE);
}

But how can we get to task_struct by the new version?

It is because we use a new current_thread_info

#include <asm/current.h>
#define current_thread_info() ((struct thread_info *)current)
#endif

The current is defined below

struct task_struct;

DECLARE_PER_CPU(struct task_struct *, current_task);

static __always_inline struct task_struct *get_current(void)
{
  return this_cpu_read_stable(current_task);
}

#define current get_current

That is to say, we have task_struct from per cpu.

Here is the last part of the source code we show

DEFINE_PER_CPU(struct task_struct *, current_task) = &init_task;

......

__visible __notrace_funcgraph struct task_struct *
__switch_to(struct task_struct *prev_p, struct task_struct *next_p)
{
......
this_cpu_write(current_task, next_p);
......
return prev_p;
}


...

#define this_cpu_read_stable(var)       percpu_stable_op("mov", var)

By this way, we can get task_struct easily.

Upvotes: 4

Maxim Egorushkin
Maxim Egorushkin

Reputation: 136208

The comment says:

    /*
     * For reasons of header soup (see current_thread_info()), this
     * must be the first element of task_struct.
     */
    struct thread_info      thread_info;

And then <linux/thread_info.h>:

/*
 * For CONFIG_THREAD_INFO_IN_TASK kernels we need <asm/current.h> for the
 * definition of current, but for !CONFIG_THREAD_INFO_IN_TASK kernels,
 * including <asm/current.h> can cause a circular dependency on some platforms.
 */
#include <asm/current.h>
#define current_thread_info() ((struct thread_info *)current)
#endif

In other words, <linux/thread_info.h> cannot include <linux/sched.h>, where task_struct is defined, so that it cannot access its members. However, it can cast a task_struct* to the pointer to its first member (C and C++ aliasing rules explicitly allow that), which is struct thread_info*.

Upvotes: 5

Related Questions