Tüm eğitimler
TEKNİK REHBER GÖMÜLÜ LİNUX KERNEL SENKRONIZASYON 2026

RCU
Read-Copy-Update Kernel Senkronizasyonu

Okuyucuları kilitsiz tutan, yazıcıları grace period ile senkronize eden Linux kernel'ın en performanslı senkronizasyon primitifi.

00 RCU neden var

Geleneksel okuyucu-yazıcı kilitleri, çok çekirdekli sistemlerde okuyucu sayısı arttıkça önbellek çatışması ve boş döngü maliyetiyle yavaşlar. RCU bu maliyeti sıfıra indirir.

rwlock ve seqlock'un sınırları

Klasik rwlock_t ile birden fazla okuyucu eş zamanlı okuyabilir, ancak her read_lock() çağrısı kilidin referans sayacını artırmak için paylaşılan bir atomik değişkene yazar. 64 çekirdekli bir sistemde 64 okuyucunun aynı cache line'a sürekli yazması, önbellek satırı ping-pong (false sharing) sorununa yol açar ve ölçeklenebilirliği dramatik biçimde düşürür.

CPU0 read_lock() → atomic_inc(rwlock.count) → cache line invalid eder
CPU1 read_lock() → cache miss → refetch → atomic_inc → invalidate
CPU2 read_lock() → cache miss → ...
→ O(N) maliyet, N = çekirdek sayısı

Lockless okuma motivasyonu

RCU'nun temel fikri şudur: okuyucu tarafında hiçbir paylaşılan değişkene yazmadan okuma yapılabilir. Bunun bedeli yazıcı tarafına yüklenir; yazıcı yeni bir kopya üretir, atomik pointer ataması yapar ve eski kopyanın güvenle silinebileceği noktayı bekler.

Okuyucu maliyetiYalnızca preemption disable/enable — önbellek yazısı yok, atomik işlem yok
Yazıcı maliyetiKopyalama + atomic pointer swap + grace period bekleme (synchronize_rcu)
Bellek modeliPointer yayını için smp_store_release, okuma için smp_load_acquire (memory barrier)
ÖlçeklenebilirlikOkuyucu sayısından bağımsız O(1) okuma maliyeti

Kullanım alanları

Linux kernel'ında RCU en yaygın biçimde aşağıdaki yapılarda kullanılır:

Alt sistemRCU kullanımı
VFS / dcacheDizin girişleri (dentry) okuma yolunda RCU ile korunur
Ağ yönlendirmeFIB (routing table) aramaları RCU okuma tarafında yapılır
Task listesifor_each_process() — PID araması RCU ile kilitsiz
Module listesiModül aramaları okuma tarafında RCU korumalı
SELinux AVCErişim vektör önbelleği RCU ile hızlı aranır
netfilterkural zinciri güncellemeleri RCU pointer swap ile atomik
TEMEL KISIT

RCU yalnızca pointer tabanlı veri yapıları için uygundur. İntegers veya skalarları doğrudan korumak için kullanılamaz. Okuyucu kritik bölgesi uyku içeremez (CONFIG_PREEMPT_RCU olmayan kernelde).

01 RCU temel modeli

Grace period, quiescent state ve publish-subscribe idiom — RCU'nun üç temel kavramı birlikte anlaşıldığında mekanizma netleşir.

Grace period ve quiescent state

RCU, eski pointer'a erişebilecek tüm okuyucuların işini bitirmesini bekler. Bu bekleme süresine grace period denir. Bir CPU'nun RCU okuma kritik bölgesi dışında olduğu her ana quiescent state (sessiz durum) denir. Tüm CPU'lar en az bir quiescent state geçirdiğinde bir grace period tamamlanmış sayılır.

Yazıcı: yeni kopya oluştur → rcu_assign_pointer() → synchronize_rcu() bekle
                                                             |
CPU0: [rcu_read_lock ... rcu_read_unlock] ← quiescent state burada
CPU1: [rcu_read_lock ... rcu_read_unlock] ← quiescent state burada
CPU2: context switch (scheduler) ← quiescent state
                                                             |
                                              grace period bitti → kfree(eski)

Quiescent state örnekleri

Bir CPU aşağıdaki durumlarda quiescent state'e girer:

Context switchScheduler başka bir process'e geçtiğinde — okuyucu kritik bölgesi içinde context switch olmaz
User spaceCPU kullanıcı alanında çalışıyorsa — kernel RCU kritik bölgesi dışındadır
IdleCPU idle döngüsündeyse — hiçbir kernel kodu çalışmıyor
OfflineCPU hotplug ile offline yapılmışsa — hiçbir RCU okuyucu kalmaz

Publish-subscribe idiom

RCU, veri yapısı güncellemelerini publish-subscribe modeli ile yapar. Yazıcı yeni veriyi yayınlar (rcu_assign_pointer), okuyucu abone olur (rcu_dereference). İkisi arasında bellek bariyeri garanti edilir.

/* Yazıcı — publish */
struct config *new_cfg = kmalloc(sizeof(*new_cfg), GFP_KERNEL);
memcpy(new_cfg, old_cfg, sizeof(*new_cfg));
new_cfg->value = 42;
rcu_assign_pointer(global_cfg, new_cfg);  /* smp_store_release içerir */

/* Okuyucu — subscribe */
rcu_read_lock();
struct config *cfg = rcu_dereference(global_cfg);  /* smp_load_acquire içerir */
do_something(cfg->value);
rcu_read_unlock();

Memory ordering garantisi

rcu_assign_pointer() içindeki smp_store_release(), önceki tüm yazmaların okuyucu tarafından görünmesini garanti eder. rcu_dereference() içindeki smp_load_acquire() ise pointer okunduktan sonra yapılan erişimlerin doğru sıralandığını garantiler. Bu çift bariyer olmadan okuyucu yarı-başlatılmış veri görebilir.

02 rcu_read_lock / rcu_read_unlock

Okuyucu tarafı API son derece hafiftir: çoğu konfigürasyonda tek instruction — preemption disable/enable.

API kullanımı

#include <linux/rcupdate.h>

rcu_read_lock();
/* RCU korumalı pointer'lara erişim */
struct my_data *p = rcu_dereference(global_ptr);
if (p)
    use(p->field);
rcu_read_unlock();

Preemption modlarına göre davranış

Kernel konfigürasyonurcu_read_lock() ne yaparUyku izni
CONFIG_PREEMPTION=n (server)Boş makro — maliyet sıfırHayır
CONFIG_PREEMPTION=y (desktop/RT)preempt_disable()Hayır
CONFIG_PREEMPT_RCUpreempt_disable() + counter güncellemeHayır
SRCU (ayrı API)srcu_read_lock(&srcu_struct)Evet

Nested okuma kritik bölgeleri

RCU okuma kritik bölgeleri iç içe geçebilir. Her rcu_read_lock() için bir rcu_read_unlock() gerekir. Preemptible RCU'da sayaç tutulur:

rcu_read_lock();          /* derinlik 1 */
  p = rcu_dereference(ptr_a);
  rcu_read_lock();        /* derinlik 2 — tamam */
    q = rcu_dereference(ptr_b);
    use(p, q);
  rcu_read_unlock();      /* derinlik 2 */
rcu_read_unlock();        /* derinlik 1 */

Yasak işlemler

Okuma kritik bölgesi içinde aşağıdakiler yasaktır:

Uyku / beklememsleep(), wait_event(), mutex_lock() — preemption disable iken uyku kernel'ı kilitler
synchronize_rcu()Grace period beklemek için okuyucu kritik bölgesinden çıkılması gerekir — deadlock
kfree() — doğrudanRCU korumalı pointer'ı okuma tarafında serbest bırakmak race condition'a yol açar

rcu_read_lock_bh / rcu_read_lock_sched

Ağ alt sisteminde BH-disabled bağlamda çalışırken rcu_read_lock_bh() kullanılır. Scheduler bağlamında rcu_read_lock_sched() tercih edilir. Bu varyantlar farklı quiescent state tanımları kullanır ve performans açısından rcu_read_lock() ile eşdeğerdir.

/* Ağ RX yolunda softirq bağlamı */
rcu_read_lock_bh();
struct neighbour *n = __ipv4_neigh_lookup_noref(dev, nexthop);
if (n)
    neigh_output(n, skb, false);
rcu_read_unlock_bh();

03 rcu_assign_pointer / rcu_dereference

Pointer yayınlama ve okuma API'si — bellek bariyerlerini doğru yerleştirerek yarı-başlatılmış veriye erişimi engeller.

rcu_assign_pointer

Yazıcı tarafında kullanılır. Yeni pointer değerini önceki tüm yazmaları görünür kılarak atomik biçimde yayınlar. Derleyici ve işlemci yeniden sıralamalarını engeller.

/* Tanım (basitleştirilmiş) */
#define rcu_assign_pointer(p, v)                    \
do {                                                \
    smp_store_release(&(p),                         \
        RCU_INITIALIZER(v));                        \
} while (0)

/* Kullanım */
struct node *new_node = kzalloc(sizeof(*new_node), GFP_KERNEL);
new_node->data = 100;
rcu_assign_pointer(list_head, new_node);

rcu_dereference

Okuyucu tarafında rcu_read_lock() ile korunan bölgede kullanılır. Alpha mimarisinde smp_read_barrier_depends(), diğer mimarilerde READ_ONCE() eşdeğeridir. Derleyicinin pointer bağımlı yüklemeleri optimize etmesini engeller.

rcu_read_lock();
struct node *p = rcu_dereference(list_head);
/* p artık güvenli — grace period boyunca geçerli */
if (p != NULL) {
    int val = p->data;    /* Bu erişim korumalı */
    pr_info("value: %d\n", val);
}
rcu_read_unlock();
/* p burada artık güvenli DEĞİL — erişim yapılamaz */

rcu_dereference_protected

Yazıcı tarafında, kilit tutulurken yapılan erişimlerde kullanılır. Sparse aracının RCU kontrollerini atlatan bir varyantıdır:

spin_lock(&update_lock);
struct node *p = rcu_dereference_protected(list_head,
                     lockdep_is_held(&update_lock));
/* Güvenle değişiklik yap */
p->data = new_value;
spin_unlock(&update_lock);

rcu_access_pointer

Pointer'ın NULL olup olmadığını kontrol etmek için rcu_read_lock() dışında kullanılabilir. Yalnızca NULL kontrolü yapılacaksa rcu_dereference() gerekmez:

/* rcu_read_lock gerekmez — sadece NULL kontrolü */
if (rcu_access_pointer(global_ptr) == NULL)
    return -ENODEV;

Sparse ile statik analiz

__rcu tip niteleyicisi sparse aracına RCU kullanımlarını kontrol ettirir. Yanlış bağlamda rcu_dereference() çağrılmadığında sparse uyarı verir:

struct config __rcu *global_cfg;  /* __rcu etiketi */

/* sparse: rcu_read_lock() olmadan erişim — uyarı */
struct config *p = global_cfg;       /* YANLIŞ */
struct config *p = rcu_dereference(global_cfg);  /* DOĞRU */

04 synchronize_rcu / call_rcu / kfree_rcu

Yazıcı, yeni pointer'ı yayınladıktan sonra eski veriyi ancak tüm okuyucuların işini bitirdiğinden emin olarak silebilir. Bu üç API farklı bağlamlara hitap eder.

synchronize_rcu — bloklu bekleme

Çağrıyı yapan thread, mevcut grace period tamamlanana kadar uyur. Process bağlamında, yüksek öncelikli kodda kullanılamaz.

struct config *old_cfg;

spin_lock(&cfg_lock);
old_cfg = rcu_dereference_protected(global_cfg,
              lockdep_is_held(&cfg_lock));
rcu_assign_pointer(global_cfg, new_cfg);
spin_unlock(&cfg_lock);

synchronize_rcu();    /* tüm okuyucular bitene kadar blokla */
kfree(old_cfg);       /* artık güvenli */

call_rcu — asenkron geri çağrım

Grace period beklemek yerine bir callback kaydeder. Softirq bağlamında, interrupt handler'dan çağrılabilir. Grace period dolduğunda callback çalışır:

struct my_obj {
    int data;
    struct rcu_head rcu;  /* callback için head alanı */
};

static void my_obj_reclaim(struct rcu_head *head)
{
    struct my_obj *obj = container_of(head, struct my_obj, rcu);
    kfree(obj);
}

/* Yazıcı — asenkron silme */
rcu_assign_pointer(global_obj, new_obj);
call_rcu(&old_obj->rcu, my_obj_reclaim);

kfree_rcu — basitleştirilmiş silme

Yalnızca kfree() yapılacaksa callback yazmak gerekmez. kfree_rcu() bunu otomatik halleder. Linux 5.12'den itibaren rcu alanı olmadan da kullanılabilir:

struct my_obj {
    int data;
    struct rcu_head rcu;
};

/* Eski yol — rcu_head gerektirir */
kfree_rcu(old_obj, rcu);

/* Linux 5.12+ — rcu_head gerektirmez */
kfree_rcu(old_obj);

synchronize_rcu_expedited

Normal synchronize_rcu()'dan daha hızlı tamamlanır — IPI (inter-processor interrupt) göndererek CPU'ları aktif olarak quiescent state'e zorlar. Sistem yükünü artırdığı için sadece test ve hata ayıklama için önerilir:

/* Hızlı ama pahalı — sadece non-production kullanım */
synchronize_rcu_expedited();

Bellek modeli özeti

Yazıcı:
  [1] new = alloc() + init
  [2] rcu_assign_pointer(ptr, new)  ← smp_store_release
  [3] synchronize_rcu()             ← tüm CPU'lar quiescent
  [4] kfree(old)                    ← güvenli

Okuyucu (grace period öncesi başlar):
  rcu_read_lock()
  p = rcu_dereference(ptr)          ← smp_load_acquire
  use(p)                            ← [3] bitmeden grace period aktif
  rcu_read_unlock()                 ← quiescent state başladı

05 SRCU — Sleepable RCU

Standart RCU okuma kritik bölgesinde uyku yasakken, SRCU (Sleepable RCU) okuyucuların uyumasına izin verir. Bu esnekliğin bedeli daha ağır başlatma ve per-srcu-struct grace period maliyetidir.

SRCU ne zaman gerekir

Uyuyan okuyucuOkuma kritik bölgesinde mutex, semaphore veya I/O beklenmesi gerekiyorsa
Filesystem notifierfsnotify gibi dosya sistemi olaylarında callback uyuyabilir
KVM vCPUGuest CPU'lar uyurken SRCU ile korunan yapılara erişim gerekebilir
Netfilter hookBazı netfilter hook'ları uyku gerektiren işlemler yapar

init_srcu_struct ve DEFINE_SRCU

#include <linux/srcu.h>

/* Statik tanım */
DEFINE_SRCU(my_srcu);

/* Dinamik tanım */
struct srcu_struct my_srcu;
int ret = init_srcu_struct(&my_srcu);
if (ret)
    return ret;

/* Temizlik */
cleanup_srcu_struct(&my_srcu);

SRCU okuyucu API

int idx;

idx = srcu_read_lock(&my_srcu);    /* integer döner — indeks */
/* Bu bölgede uyku mümkün */
struct data *p = srcu_dereference(global_data, &my_srcu);
if (p) {
    mutex_lock(&p->lock);          /* uyku — SRCU'da izinli */
    process(p);
    mutex_unlock(&p->lock);
}
srcu_read_unlock(&my_srcu, idx);  /* indeks geri verilir */

synchronize_srcu ve call_srcu

/* Bloklu — bu srcu_struct için tüm okuyucuları bekle */
synchronize_srcu(&my_srcu);

/* Asenkron callback */
call_srcu(&my_srcu, &old_obj->rcu, my_reclaim);

/* Hızlı (expedited) varyant */
synchronize_srcu_expedited(&my_srcu);

SRCU ile standart RCU farkı

ÖzellikRCUSRCU
Okuyucu uykuYasakİzinli
Okuyucu maliyeti~0 (preempt_disable)Per-CPU sayaç güncelleme
Grace periodGlobalPer-srcu_struct
Birden fazla domainYokHer srcu_struct bağımsız
init gerektirmeStatik, init yokinit_srcu_struct() gerekli
Kullanım alanıGenel amaçUyuyan okuyucu senaryoları

SRCU iç mekanizması

SRCU iki adet per-CPU sayaç çifti tutar (A ve B). Okuyucular aktif çifte kaydolur. synchronize_srcu() çağrıldığında geçiş yapılır ve eski çiftteki sayaçların sıfırlanması beklenir. Bu mekanizma preemption disable gerektirmez çünkü CPU'ların quiescent state'ini beklemez; doğrudan okuyucu sayısını takip eder.

06 RCU-protected linked list

Linux kernel, RCU ile korunan bağlı listeler için özel API sağlar. Bu API standart list API'sine paralel bir arayüz sunar.

Temel API

list_add_rcu()Listeye yeni eleman ekler — smp_store_release ile pointer güncellemesi
list_del_rcu()Elemanı listeden çıkarır — ancak kfree için grace period beklenmeli
list_replace_rcu()Bir elemanı atomik olarak başkasıyla değiştirir
list_for_each_entry_rcu()RCU okuma kritik bölgesinde listeyi güvenle iterate eder

Tam örnek — global cihaz listesi

#include <linux/list.h>
#include <linux/rcupdate.h>
#include <linux/spinlock.h>

struct device_entry {
    int id;
    char name[32];
    struct list_head node;
    struct rcu_head rcu;
};

static LIST_HEAD(device_list);
static DEFINE_SPINLOCK(device_lock);

/* Okuyucu — kilit yok, sadece rcu_read_lock */
struct device_entry *find_device(int id)
{
    struct device_entry *entry;

    rcu_read_lock();
    list_for_each_entry_rcu(entry, &device_list, node) {
        if (entry->id == id) {
            rcu_read_unlock();
            return entry;  /* Dikkat: caller grace period bitince erişmemeli */
        }
    }
    rcu_read_unlock();
    return NULL;
}

/* Yazıcı — spinlock + rcu_assign */
int add_device(int id, const char *name)
{
    struct device_entry *entry = kzalloc(sizeof(*entry), GFP_KERNEL);
    if (!entry)
        return -ENOMEM;

    entry->id = id;
    strscpy(entry->name, name, sizeof(entry->name));
    INIT_LIST_HEAD(&entry->node);

    spin_lock(&device_lock);
    list_add_rcu(&entry->node, &device_list);
    spin_unlock(&device_lock);
    return 0;
}

/* Yazıcı — silme */
static void device_free_rcu(struct rcu_head *head)
{
    struct device_entry *entry =
        container_of(head, struct device_entry, rcu);
    kfree(entry);
}

void remove_device(int id)
{
    struct device_entry *entry;

    spin_lock(&device_lock);
    list_for_each_entry(entry, &device_list, node) {
        if (entry->id == id) {
            list_del_rcu(&entry->node);
            spin_unlock(&device_lock);
            call_rcu(&entry->rcu, device_free_rcu);
            return;
        }
    }
    spin_unlock(&device_lock);
}

hlist_for_each_entry_rcu

Hash table yapıları için hlist varyantları da mevcuttur. Kernel'ın pid hash table'ı bu yapıyı kullanır:

struct hlist_head hash[256];

/* Ekleme */
spin_lock(&hash_lock);
hlist_add_head_rcu(&obj->hnode, &hash[h]);
spin_unlock(&hash_lock);

/* Arama */
rcu_read_lock();
hlist_for_each_entry_rcu(obj, &hash[h], hnode) {
    if (obj->key == key)
        break;
}
rcu_read_unlock();

list_for_each_entry_rcu ile concurrent güncelleme

Okuyucu iterate ederken yazıcı eleman ekleyebilir/silebilir. list_for_each_entry_rcu() makrosu READ_ONCE() ile her adımda pointer okur, böylece yeni eklenen elemanları görebilir ancak tutarsız pointer görmez.

07 RCU vs diğer mekanizmalar

RCU her senaryoya uygun değildir. Spinlock, seqlock ve hazard pointer ile karşılaştırmalı analiz doğru aracı seçmeye yardımcı olur.

Kapsamlı karşılaştırma tablosu

Özellik spinlock rwlock seqlock RCU Hazard ptr
Okuyucu maliyetiSpin (yüksek)Atomic inc/dec2x okuma (retry)~SıfırStore + fence
Yazıcı maliyetiSpinExclusive lockSeqcount güncelleKopya + GP beklemeSpin + tarama
Okuyucu ölçeğiKötüOrtaİyiMükemmelİyi
Okuyucu uykuHayırHayırHayırHayır (SRCU: Evet)Hayır
Bellek overheadMinimalMinimalMinimalEski kopya grace period süresincePer-thread hazard ptr
Veri büyüklüğüHer büyüklükHer büyüklükKüçük/skalarPointer tabanlıPointer tabanlı
Yazıcı sayısıTekTekTek/çokluÇoklu (kilit ile)Çoklu
Kullanım alanıKısa kritik bölgeÇok okuyucuKüçük veri, çok okumaÇok okuyucu, nadir yazmaLock-free yapılar

seqlock ile RCU karşılaştırması

seqlock, okuyucunun tutarsız veri okumasını tespit edip yeniden denemesine dayanır. Küçük, hızlı okunabilen veri (zaman damgaları, sayaçlar) için idealdir. RCU ise büyük veri yapıları için uygundur çünkü okuyucu retry gerekmez.

/* seqlock okuyucu */
unsigned int seq;
do {
    seq = read_seqbegin(&my_seqlock);
    memcpy(&local_copy, &shared_data, sizeof(local_copy));
} while (read_seqretry(&my_seqlock, seq));

/* RCU okuyucu — retry yok */
rcu_read_lock();
p = rcu_dereference(global_ptr);
memcpy(&local_copy, p, sizeof(*p));
rcu_read_unlock();

Hangi durumda ne kullanmalı

spinlockKısa kritik bölge, interrupt context, az çekirdekli sistem
rwlockÇok okuyucu orta ölçekli sistem, eski kod uyumluluğu
seqlockKüçük veri (jiffies, timespec), sık okuma, nadir yazma
RCUPointer tabanlı yapı, ölçeklenebilir okuma, okuma >> yazma
SRCUOkuma kritik bölgesinde uyku gereken durum

Overhead ölçümü

Bir 64 çekirdekli sunucuda 100 ns periyotla yapılan ölçümlerde RCU okuyucu maliyeti spinlock'tan ~50x daha düşük çıkar. Yazıcı tarafında synchronize_rcu() birkaç ms sürebilir — bu süre sistemdeki en uzun scheduler tick periyoduna bağlıdır.

08 Hata ayıklama

RCU hataları — grace period ihlali, use-after-free, okuma kritik bölgesinde uyku — sessiz veri bozulmasına yol açar. Kernel, bu hataları yakalamak için çeşitli araçlar sunar.

CONFIG_PROVE_RCU ve lockdep

Lockdep'in RCU uzantısı, yanlış bağlamda rcu_dereference() kullanımını, rcu_read_lock() içinde synchronize_rcu() çağrısını ve __rcu işaretli pointer'lara korumasız erişimi tespit eder:

CONFIG_DEBUG_LOCK_ALLOC=y
CONFIG_PROVE_LOCKING=y
CONFIG_PROVE_RCU=y
CONFIG_RCU_TRACE=y

Bu konfigürasyonlarla kernel, illegal RCU kullanımını stack trace ile birlikte loglar:

[ 1234.567890] WARNING: suspicious RCU usage
[ 1234.567891] kernel/sched/core.c:1234
[ 1234.567892] rcu_dereference() called without rcu_read_lock()
[ 1234.567893] Call Trace:
[ 1234.567894]  dump_stack+0x48
[ 1234.567895]  lockdep_rcu_suspicious+0x12c
[ 1234.567896]  my_driver_read+0x34

rcutorture — stres testi modülü

rcutorture kernel modülü RCU implementasyonunu sistematik biçimde test eder. Writer ve reader thread'leri yoğun şekilde çalıştırarak grace period ve memory ordering'i doğrular:

# rcutorture modülünü yükle
modprobe rcutorture torture_type=rcu nreaders=8 nfakewriters=4 \
         stutter=5 irqreader=1 stat_interval=60

# Test sonuçlarını izle
dmesg | grep -i torture

# Temizle
echo 0 > /sys/module/rcutorture/parameters/shutdown_secs

RCU stall — CPU askıda kalması

Bir CPU uzun süre quiescent state'e girmezse (varsayılan 21 saniye) kernel RCU stall mesajı basar. Bu genellikle softirq, NMI veya interrupt handler içinde uzun döngü anlamına gelir:

[ 5678.901234] INFO: rcu_sched detected stalls on CPUs/tasks: { 3 } (detected by 0, 21016 jiffies)
[ 5678.901235] Sending NMI from CPU 0 to CPUs 3:
[ 5678.901236] NMI backtrace for cpu 3
[ 5678.901237] Call Trace:
[ 5678.901238]  my_broken_handler+0x8c    <-- sonsuz döngü burada

# Stall timeout ayarı (saniye)
echo 60 > /sys/module/rcupdate/parameters/rcu_cpu_stall_timeout

RCU istatistikleri — debugfs

# RCU state bilgisi
cat /sys/kernel/debug/rcu/rcu_sched/rcudata

# Grace period sayacı
cat /sys/kernel/debug/rcu/rcu_sched/rcu_gp

# SRCU istatistikleri
cat /sys/kernel/debug/rcu/rcu_srcu

Yaygın hatalar ve çözümleri

HataBelirtiÇözüm
rcu_read_unlock() eksikRCU stall, grace period hiç bitmezHer lock/unlock çiftini eşle, PROVE_RCU kullan
Grace period beklemeden kfreeUse-after-free, KASAN uyarısısynchronize_rcu() veya kfree_rcu() kullan
Kritik bölgede uykuKernel panic (might_sleep kontrolü)SRCU'ya geç veya uyku kodu dışarı taşı
rcu_dereference() dışarıdaAlpha'da veri bozulması, PROVE_RCU uyarısıHer erişim rcu_read_lock() içinde olmalı
call_rcu callback'te kilitDeadlock — softirq bağlamında mutex yasakCallback'te yalnızca spinlock kullan

KCSAN ile veri yarışı tespiti

Kernel Concurrency Sanitizer (KCSAN), RCU olmadan yapılan eş zamanlı bellek erişimlerini tespit eder. CONFIG_KCSAN=y ile derlenen kernelde veri yarışları raporlanır:

CONFIG_KCSAN=y
CONFIG_KCSAN_REPORT_ONCE_IN_MS=3000

# Çıktı örneği:
# KCSAN: data-race in my_func / my_func
# write to 0xffff... by task 123:
# read to 0xffff... by task 456:
ÖNERİ

Geliştirme sırasında CONFIG_PROVE_RCU, CONFIG_DEBUG_LOCK_ALLOC ve CONFIG_KCSAN birlikte etkinleştirin. Bu üç araç birlikte RCU kaynaklı hataların büyük çoğunluğunu derleme veya ilk çalışma anında yakalar.