rcu(READ COPY UPDATE)


a下のbのnameを更新するrcuのサンプルです。cにbを読込み更新し、rcuコールバックでcと差し替えます。リストの要素はstatic変数としていますが、実際は動的に取得し、rcuコールバックで解放する処理が実装されます。
#include <linux/kernel.h>
#include <linux/rcupdate.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/swap.h>

struct hoge {
       struct rcu_head hoge_rcu;
       struct hoge     *down;
       char    name[6];
       struct hoge     *rcu_tmp;
};

static  struct hoge a;
static  struct hoge b;
static  struct hoge c;

static void hoge_rcu_callback(struct rcu_head *head);

static  void    printk_hoge(char* msg, struct hoge* a)
{
       struct hoge *tmp;

       tmp = a->down;
       printk("%s:%s\n", msg, tmp->name);
}

static int __init hoge_rcu_init(void)
{
       strcpy(a.name, "hoge1");
       strcpy(b.name, "hoge2"); 
       a.down= &b;

       rcu_read_lock();
       memcpy((char *)&c, (char *)a.down, sizeof(struct hoge));
       strcpy(c.name, "hoge3");
       a.rcu_tmp = &c;
       rcu_read_unlock();


       printk_hoge("before call rcu", &a);
       call_rcu(&a.hoge_rcu, hoge_rcu_callback);
       schedule_timeout_interruptible(100);
       printk_hoge("after call rcu", &a);

       return -1;
}

static void hoge_rcu_callback(struct rcu_head *head)
{
       struct hoge *hoge =
               container_of(head, struct hoge, hoge_rcu);

       hoge->down = hoge->rcu_tmp;
}

MODULE_LICENSE("GPL");
module_init(hoge_rcu_init);
動作
[root@localhost lkm]# dmesg
  :
[ 4669.301854] before call rcu:hoge2
[ 4669.402079] after call rcu:hoge3
rcuの初期化で、open_softirq()でソフト割り込みにrcu_process_callbacks()を設定します。rcuは厳密に言えば、タスクレットでなくソフト割り込みでの実装となります。(実装は同じですが。)
void __init rcu_init(void)
{
       int cpu;

       rcu_bootup_announce();
       rcu_init_one(&rcu_sched_state, &rcu_sched_data);
       rcu_init_one(&rcu_bh_state, &rcu_bh_data);
       __rcu_init_preempt();
        open_softirq(RCU_SOFTIRQ, rcu_process_callbacks);

       cpu_notifier(rcu_cpu_notify, 0);
       for_each_online_cpu(cpu)
               rcu_cpu_notify(NULL, CPU_UP_PREPARE, (void *)(long)cpu);
       check_cpu_stall_init();
}

enum
{
       HI_SOFTIRQ=0,
       TIMER_SOFTIRQ,
       NET_TX_SOFTIRQ,
       NET_RX_SOFTIRQ,
       BLOCK_SOFTIRQ,
       BLOCK_IOPOLL_SOFTIRQ,
       TASKLET_SOFTIRQ,
       SCHED_SOFTIRQ,
       HRTIMER_SOFTIRQ,
       RCU_SOFTIRQ,

       NR_SOFTIRQS
};

void open_softirq(int nr, void (*action)(struct softirq_action *))
{
       softirq_vec[nr].action = action;
}

static void rcu_process_callbacks(struct softirq_action *unused)
{
       __rcu_process_callbacks(&rcu_sched_ctrlblk);
       __rcu_process_callbacks(&rcu_bh_ctrlblk);
       rcu_preempt_process_callbacks();
}
call_rcu()は更新コールバック関数をrcu_headに設定し、rcu_sched_stateにリストし、ソフト割り込みから起動されるrcu_process_callbacks()が、このリスト下のhead->funcをコールします。
#define call_rcu        call_rcu_sched

void call_rcu_sched(struct rcu_head *head, void (*func)(struct rcu_head *rcu))
{
       __call_rcu(head, func, &rcu_sched_state);
}
EXPORT_SYMBOL_GPL(call_rcu_sched);

static void __call_rcu(struct rcu_head *head,
                      void (*func)(struct rcu_head *rcu),
                      struct rcu_ctrlblk *rcp)
{
       unsigned long flags;

       debug_rcu_head_queue(head);
       head->func = func;
       head->next = NULL;

       local_irq_save(flags);
       *rcp->curtail = head;
       rcp->curtail = &head->next;
       RCU_TRACE(rcp->qlen++);
       local_irq_restore(flags);
}

補足

読込み中は、rcu_read_lock()/rcu_read_unlock()します。rcu_read_lock()はプリエンプション禁止/rcu_read_unlock()は許可するだけで、プリエンプション禁止だと、ハード割込みによる非同期カーネルパスの偏移はありません。ソフト割込みの発信元は、ハード割込みです。従ってプリエンプション禁止の間は、rcu更新のコールバックが動作しません。

rcuの実際の利用は、リント要素の削除に特化しており、要素の更新は、メモリの取得/解放の負荷を考慮してか、ロックでの実装が主のようです。


最終更新 2015/04/10 19:29:30 - north
(2013/04/15 18:06:58 作成)


検索

アクセス数
3689308
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。