cookie的原理借助于unsigned long型,和refcount_t引用计数器。
| 32位 | 64位 | |
|---|---|---|
| char * | 4字节 | 8字节 | 
| unsigned long | 4字节 | 8字节 | 
数据结构修改
首先看看实现core scheduling功能对数据结构有哪些修改
task_struct
struct task_struct{
    struct rb_node core_node;
    unsigned long core_cookie;
    unsigned int core_occupation;
}
sched_statistics
struct sched_statistics{
    u64 core_forceidle_sum;
}
sched_core_cookie
cookie本质就是一个内核引用计数器refcount_t成员构成的struct结构体。然后task_struct中保存的core_cookie是unsigned long型数据,刚好能存放指针变量值,就是存放的该结构体的内存地址。
使用alloc分配出来的sched_core_cookie结构体的address内存地址作为一个task的cookie值,cookie 类型是unsigned long的指针。
/*新增*/
struct sched_core_cookie{
    refcount_t refcnt;
}
rq
rq中增加了有关core sched的开关等信息。
struct rq {
    //...省略
#ifdef CONFIG_SCHED_CORE
    /* per rq */
    struct rq       *core;
    struct task_struct  *core_pick;
    unsigned int        core_enabled;
    unsigned int        core_sched_seq;
    struct rb_root      core_tree;        /*可以使用同一个core的都要加入红黑树*/
    /* shared state -- careful with sched_core_cpu_deactivate() */
    unsigned int        core_task_seq;
    unsigned int        core_pick_seq;
    unsigned long       core_cookie;
    unsigned int        core_forceidle_count;
    unsigned int        core_forceidle_seq;
    unsigned int        core_forceidle_occupation;
    u64         core_forceidle_start;
#endif
}
这部分初始化
#ifdef CONFIG_SCHED_CORE
        rq->core = rq;
        rq->core_pick = NULL;
        rq->core_enabled = 0;
        rq->core_tree = RB_ROOT;
        rq->core_forceidle_count = 0;
        rq->core_forceidle_occupation = 0;
        rq->core_forceidle_start = 0;
        rq->core_cookie = 0UL;
#endif
cfs_rq
struct cfs_rq{
    //...省略代码
    #ifdef CONFIG_SCHED_CORE
    unsigned int        forceidle_seq;
    u64         min_vruntime_fi;
#endif
}
cookie
用户态程序通过prctl系统调用来操作相关进程的cookie动作。
prctl接口
prctl系统调用接口中新增有关PR_SCHED_CORE的操作处理函数。
SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
        unsigned long, arg4, unsigned long, arg5)
{
    ...
#ifdef CONFIG_SCHED_CORE
    case PR_SCHED_CORE:
        error = sched_core_share_pid(arg2, arg3, arg4, arg5);
        break;
#endif
}
int sched_core_share_pid(unsigned int cmd, pid_t pid, enum pid_type type,
             unsigned long uaddr)
{
    unsigned long cookie = 0, id = 0;
    struct task_struct *task, *p;
    struct pid *grp;
    int err = 0;
    //...省略
    rcu_read_lock();
    if (pid == 0) {
        task = current;        /*pid为0,则是设置进程本身*/
    } else {
        task = find_task_by_vpid(pid);    /*根据pid查找进程PCB*/
        if (!task) {
            rcu_read_unlock();
            return -ESRCH;
        }
    }
    get_task_struct(task);    /*该函数就是为了给task_struct引用符计数+1,防止中断处理中程序被kill掉访问非法PCB指针*/
    rcu_read_unlock();
    /* 检查进程是否有权限修改指定的进程
     * Check if this process has the right to modify the specified
     * process. Use the regular "ptrace_may_access()" checks.
     */
    if (!ptrace_may_access(task, PTRACE_MODE_READ_REALCREDS)) {
        err = -EPERM;
        goto out;
    }
    switch (cmd) {
    case PR_SCHED_CORE_GET:        
        if (type != PIDTYPE_PID || uaddr & 7) {
            err = -EINVAL;
            goto out;
        }
        cookie = sched_core_clone_cookie(task);        /*获取task任务的cookie值*/
        if (cookie) {
            /* XXX improve ? */
            ptr_to_hashval((void *)cookie, &id);
        }
        err = put_user(id, (u64 __user *)uaddr);
        goto out;
    case PR_SCHED_CORE_CREATE:
        cookie = sched_core_alloc_cookie();        /*创建task任务的cookie值*/
        if (!cookie) {
            err = -ENOMEM;
            goto out;
        }
        break;
    case PR_SCHED_CORE_SHARE_TO:
        cookie = sched_core_clone_cookie(current);        /*共享继承指定task任务的cookie值*/
        break;
    case PR_SCHED_CORE_SHARE_FROM:
        if (type != PIDTYPE_PID) {
            err = -EINVAL;
            goto out;
        }
        cookie = sched_core_clone_cookie(task);
        __sched_core_set(current, cookie);            /*将自身cookie共享给指定的task任务*/
        goto out;
    default:
        err = -EINVAL;
        goto out;
    };
    if (type == PIDTYPE_PID) {
        __sched_core_set(task, cookie);        /*设置任务task的cookie值*/
        goto out;
    }
    read_lock(&tasklist_lock);                /*这里是对任务组进行设置,对任务组中每个任务都设置cookie*/
    grp = task_pid_type(task, type);
    do_each_pid_thread(grp, type, p) {
        if (!ptrace_may_access(p, PTRACE_MODE_READ_REALCREDS)) {
            err = -EPERM;
            goto out_tasklist;
            goto out_tasklist;
        }
    } while_each_pid_thread(grp, type, p);
    do_each_pid_thread(grp, type, p) {
        __sched_core_set(p, cookie);
    } while_each_pid_thread(grp, type, p);
out_tasklist:
    read_unlock(&tasklist_lock);
out:
    sched_core_put_cookie(cookie);        /*cookie指针计数器减1,并检查是否为0,没有人用则需要释放*/
    put_task_struct(task);                /*检查task指针技术减1*/
    return err;
}
该函数中,out出口时为何需要对cookie进行put,对task进行put?
- 对task进行put是因为调用了get_task_struct(task)函数,该函数中会对task引用计数加1,所以在sched_core_share_pid()函数末尾需要对task指针引用计数减1
- 对cookie计数减1,是因为在get_cookie,clone_cookie,allock_cookie等函数中对cookie的引用计数加了1,然后再**__sched_core_set函数中又调用get_cookie,又再次对cookie的计数加了1,一共加了两次,所以函数末尾需要相应的对cookie计数减1并检查是否为0(__sched_core_set**函数中也有一次减1操作,不过是对old_cookie的操作)。所以最终cookie的引用次数整体还是加了1的
  
创建cookie
何时需要创建cookie?是每个进程都有一个cookie么?cookie值保存在哪里?
static unsigned long sched_core_alloc_cookie(void)
{
    struct sched_core_cookie *ck = kmalloc(sizeof(*ck), GFP_KERNEL);
    if (!ck)
        return 0;
    refcount_set(&ck->refcnt, 1);    /*cookie的计数器加1*/
    sched_core_get();                /*enable core sched功能,就是打开开关标志*/
    return (unsigned long)ck;
}

获取cookie
static unsigned long sched_core_get_cookie(unsigned long cookie)
{
    struct sched_core_cookie *ptr = (void *)cookie;
    if (ptr)
        refcount_inc(&ptr->refcnt);
    return cookie;
}
入参:某个task_struct的cookie值,p->core_cookie; 类型unsigned long存放的其实是指针,该指针指向结构体sched_core_cookie计数器。
get_cookie的操作主要就是对cookie中的refcnt计数器加1。
共享cookie
task任务共享当前current任务的cookie
cookie = sched_core_clone_cookie(current);
__sched_set_core(task, cookie);
static unsigned long sched_core_clone_cookie(struct task_struct *p)
{
    unsigned long cookie, flags;
    raw_spin_lock_irqsave(&p->pi_lock, flags);
    cookie = sched_core_get_cookie(p->core_cookie);    //获取指定进程的core_cookie值
    raw_spin_unlock_irqrestore(&p->pi_lock, flags);
    return cookie;
}
分享cookie
分享当前current任务的cookie给指定任务task。
cookie = sched_core_clone_cookie(task);
__sched_core_set(current, cookie);
清除cookie
static void sched_core_put_cookie(unsigned long cookie)
{
    struct sched_core_cookie *ptr = (void *)cookie;
    if (ptr && refcount_dec_and_test(&ptr->refcnt)) {
        kfree(ptr);
        sched_core_put();
    }
}
当清除task_struct时,也要记得清除其中的cookie
void sched_core_free(struct task_struct *p)
{
    sched_core_put_cookie(p->core_cookie);
}
父子进程cookie关系
在fork系统调用中,copy_process创建新进程的时候,子进程的cookie值继承父进程的cookie值。
void sched_core_fork(struct task_struct *p)
{
    RB_CLEAR_NODE(&p->core_node);        /*core_node红黑树节点清空*/
    p->core_cookie = sched_core_clone_cookie(current);    /*子进程cookie = 父进程cookie*/
}
周期性调度器
scheduler_tick()周期性调度器中也会涉及到core sched的操作。



















