Kernel Debug Araçları
TEKNİK REHBER KERNEL DEBUG SENKRONIZASYON 2026

lockdep & Hung Task —
deadlock tespiti.

Kernel'ın yerleşik kilit dedektörü, deadlock'u oluşmadan önce bulur. lockdep bağımlılık grafını çizer; sen sadece raporu okursun.

00 lockdep nedir — lock dependency graph

lockdep (Lock Dependency Validator), kernel kilit alımlarını izleyerek ileride deadlock'a yol açabilecek kilit sıralama ihlallerini tespit eden bir runtime doğrulama aracıdır. Deadlock'un kendisi oluşmadan, tehlikeli kilit alım sırasını önceden saptar.

Temel fikir: lock dependency graph

  Thread A: lock(L1) → lock(L2)   →  L1 → L2 bağımlılığı oluşur
  Thread B: lock(L2) → lock(L1)   →  L2 → L1 bağımlılığı oluşur

  lockdep bu grafikte çevrim (cycle) tespit eder:
  L1 → L2 → L1  ← döngüsel bağımlılık = olası deadlock!

  Gerçek deadlock Thread A ve B aynı anda çalıştığında oluşur.
  lockdep bunu Thread B'nin ilk kez L2 → L1 yaptığında bildirir.
    

lockdep özellikleri

Lock sınıfı
lockdep, her kilit örneğini değil sınıfını izler. Aynı türden tüm kilitler (örn. tüm spinlock_t örnekleri) aynı sınıfta değerlendirilir. Bu, ölçeklenebilirlik sağlar.
AA deadlock
Aynı kilidi iki kez almaya çalışmak (recursion deadlock). lockdep bunu anında bildirir.
AB-BA deadlock
İki farklı thread'in aynı iki kilidi ters sırada alması. Klasik deadlock senaryosu.
Kilit bağlamı
Interrupt handler'da mutex kullanmak gibi geçersiz bağlam hataları.

Gerekli kernel konfigürasyonu

Kconfig
CONFIG_LOCKDEP=y
CONFIG_PROVE_LOCKING=y           # lockdep'i etkinleştirir
CONFIG_DEBUG_LOCKDEP=y           # ek lockdep doğrulamaları
CONFIG_LOCK_STAT=y               # kilit istatistikleri
CONFIG_DEBUG_SPINLOCK=y          # spinlock doğrulama
CONFIG_DEBUG_MUTEXES=y           # mutex doğrulama
CONFIG_DEBUG_RT_MUTEXES=y
CONFIG_DEBUG_ATOMIC_SLEEP=y      # uyku yasak bağlamda uyku kontrolü
PERFORMANS ETKİSİ

lockdep, her kilit alımında ve bırakımında bir bağımlılık grafiği güncellemesi yapar. Bu, yaklaşık 1.5-2x CPU overhead ve belirgin miktarda ek RAM (lock class hash tablosu) anlamına gelir. Yalnızca debug kernel'larında etkinleştir.

01 lockdep uyarısını okumak

lockdep bir sorun tespit ettiğinde dmesg'e kapsamlı bir rapor yazar. Bu raporu sistematik olarak okumayı öğrenmek, sorunun kaynağını hızla bulmak için gereklidir.

dmesg — lockdep raporu
======================================================
WARNING: possible circular locking dependency detected
6.6.0-rc4 #1 Not tainted
------------------------------------------------------
kworker/0:1/42 is trying to acquire lock:
ffff000012345678 (&lock_B){+.+.}-{3:3}, at: func_b+0x30/0x60
          ^           ^      ^
          kilit adresi sınıf  kilit türü ve derinlik bilgisi

but task is already holding lock:
ffff000087654321 (&lock_A){+.+.}-{3:3}, at: func_a+0x20/0x80
          ^
          bu kilit zaten tutulmuş

which lock already depends on the new lock.

the existing dependency chain (in reverse order) is:

-> #1 (&lock_B){+.+.}-{3:3}:
       lock_acquire
       mutex_lock_nested
       func_b+0x30/0x60   ← lock_B alındı
       func_entry+0x50/0xa0

-> #0 (&lock_A){+.+.}-{3:3}:
       lock_acquire
       mutex_lock_nested
       func_a+0x20/0x80   ← lock_A alındı

other info that might help us debug this:
 Possible unsafe locking scenario:

      CPU0                    CPU1
      ----                    ----
 lock(&lock_A);           lock(&lock_B);
                           lock(&lock_A);   ← CPU1 bekler
 lock(&lock_B);   ← CPU0 bekler
                           ↑↑↑ DEADLOCK ↑↑↑

Call Trace:
 lockdep_circular_chain_check
 validate_chain
 __lock_acquire
 lock_acquire
 mutex_lock_nested
 func_a+0x20/0x80

Stack backtrace:
 func_a+0x20/0x80
 my_driver_ioctl+0x60/0x100 [my_driver]
 vfs_ioctl+0x4a/0xa0
======================================================

Raporu okuma rehberi

is trying to acquire lock
Şu an alınmak istenen kilit. Bu noktada lockdep sorunu fark etti.
but task is already holding lock
Görevin halihazırda tuttuğu kilit. Deadlock potansiyeli burada başlıyor.
Possible unsafe locking scenario
CPU0 ve CPU1'in hangi sırada kilit aldığını gösterir. Döngüsel bağımlılığı buradan oku.
Call Trace
lockdep'in uyarıyı tetiklediği noktanın yığın izi.

02 Spinlock vs mutex hataları

Kernel'da kilit türü seçimi bağlama bağlıdır. Yanlış bağlamda yanlış kilit kullanmak lockdep tarafından yakalanır.

Kilit türü seçim kuralları

BağlamSpinlockMutexSebep
Process context (görev)KullanılabilirTercih edilirMutex uyku destekler, CPU kullanmaz
Interrupt handler (IRQ)Zorunlu (spin_lock_irqsave)YasakIRQ bağlamında uyku yasak
Softirq / taskletZorunlu (spin_lock_bh)YasakSoftirq'da uyku yasak
NMI handlerYasak (deadlock riski)YasakNMI her şeyi keser

IRQ bağlamında mutex kullanımı

C — hatalı kod
#include <linux/mutex.h>
#include <linux/interrupt.h>

static DEFINE_MUTEX(my_mutex);

/* IRQ handler — bu bağlamda uyku yasak */
static irqreturn_t my_irq_handler(int irq, void *dev_id)
{
    mutex_lock(&my_mutex);   /* HATA: IRQ context'te mutex kullanılamaz */
    /* ... */
    mutex_unlock(&my_mutex);
    return IRQ_HANDLED;
}
dmesg — lockdep uyarısı
BUG: sleeping function called from invalid context
in_atomic(): 1, irqs_disabled(): 1, non_block: 0, pid: 0, name: swapper
preempt_count: 1, expected: 0
RCU nest depth: 0, expected: 0
Call Trace:
 __might_resched
 __might_sleep
 mutex_lock
 my_irq_handler+0x30/0x60   ← IRQ handler'da mutex_lock

Düzeltilmiş kod — spinlock kullan

C — doğru kod
#include <linux/spinlock.h>

static DEFINE_SPINLOCK(my_lock);

static irqreturn_t my_irq_handler(int irq, void *dev_id)
{
    unsigned long flags;

    spin_lock_irqsave(&my_lock, flags);   /* IRQ'yu durdur + kilit al */
    /* kritik bölge */
    spin_unlock_irqrestore(&my_lock, flags);
    return IRQ_HANDLED;
}

/* Process context'ten aynı kilide erişirken de irqsave kullan */
void my_process_func(void)
{
    unsigned long flags;
    spin_lock_irqsave(&my_lock, flags);   /* IRQ handler ile aynı kilit */
    /* kritik bölge */
    spin_unlock_irqrestore(&my_lock, flags);
}

03 Hung task detector

Hung task detector, TASK_UNINTERRUPTIBLE durumunda uzun süre bekleyen görevleri tespit eder. Bu durum genellikle deadlock, sonsuz bekleyen I/O işlemi veya kernel bug'ının belirtisidir.

Konfigürasyon

Kconfig
CONFIG_DETECT_HUNG_TASK=y
CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=120  # varsayılan: 120 saniye
CONFIG_BOOTPARAM_HUNG_TASK_PANIC=n    # sadece uyar, panic atma
# veya panic için:
CONFIG_BOOTPARAM_HUNG_TASK_PANIC=y

Hung task uyarısı

dmesg — hung task uyarısı
[ 1234.567890] INFO: task kworker/0:1:42 blocked for more than 120 seconds.
[ 1234.567900]       Not tainted 6.6.0 #1
[ 1234.567910] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
[ 1234.567920] task:kworker/0:1     state:D stack:13696 pid:42
[ 1234.567930] Call Trace:
[ 1234.567940]  __schedule+0x3a4/0x9c0
[ 1234.567950]  schedule+0x5c/0xa0
[ 1234.567960]  schedule_preempt_disabled+0x14/0x20
[ 1234.567970]  __mutex_lock.isra.0+0x2dc/0x4b0
[ 1234.567980]  mutex_lock_nested+0x34/0x40
[ 1234.567990]  my_driver_work_handler+0x44/0xa0 [my_driver]  ← burada bekleniyor
[ 1234.568000]  process_one_work+0x17c/0x3c0

Hung task yorumlama

state:D — TASK_UNINTERRUPTIBLE (D: Deep sleep, sinyal de uyandırmaz). Bu durumun başlıca nedenleri:

Mutex/semaphore bekleme
Kilit başka bir görev tarafından tutulmuş ve asla bırakılmıyor. Deadlock senaryosu.
I/O bekleme
Blok cihaz yanıt vermedi — disk arızası, firmware timeout. iowait yüksekse bu sebeple.
NFS/network I/O
Ağ dosya sistemi isteği yanıtsız kaldı. nfsd veya ağ sorunu.
Kernel bug
Kilit yanlış alındı ve hiç bırakılmadı.

04 hung_task_timeout_secs — özelleştirme

Hung task detector'ün zaman aşımı sysctl ile çalışma zamanında ayarlanabilir. Farklı sistemlerin farklı bekleme toleransları vardır.

bash
# Mevcut timeout değerini gör
cat /proc/sys/kernel/hung_task_timeout_secs
# 120

# Zaman aşımını azalt (hızlı tespit — test için)
echo 30 > /proc/sys/kernel/hung_task_timeout_secs

# Zaman aşımını artır (uzun I/O işlemleri olan sistemler için)
echo 300 > /proc/sys/kernel/hung_task_timeout_secs

# Hung task tespitini devre dışı bırak (tavsiye edilmez)
echo 0 > /proc/sys/kernel/hung_task_timeout_secs

# Kalıcı ayar
echo 'kernel.hung_task_timeout_secs = 60' >> /etc/sysctl.conf
sysctl -p

# Hung task panic — tespit edilince sistem dump alsın
echo 1 > /proc/sys/kernel/hung_task_panic

Ek parametreler

hung_task_check_count
Her kontrol döngüsünde incelenecek görev sayısı. Çok sayıda görev olan sistemlerde overhead kontrolü için.
hung_task_warnings
Kaç kez uyarı verileceği (varsayılan: 10). Sonraki uyarılar bastırılır.
hung_task_all_cpu_backtrace
1 = tüm CPU'ların yığın izini yazdır. Karmaşık deadlock senaryolarında faydalı.
GÖMÜLÜ SİSTEMLER

I/O yavaş veya gerçek zamanlı kısıtlamaları olan gömülü sistemlerde hung task timeout'u dikkatli ayarla. Yüksek CPU yükünde bile D durumundaki görevler normal olabilir (flash yazma, ağ I/O). Çok kısa timeout yanlış alarmlar üretir.

05 RCU stall — uzun read-side critical section

RCU (Read-Copy-Update), kilitsiz okuma için tasarlanmış bir senkronizasyon mekanizmasıdır. Ancak read-side critical section'lar çok uzun sürerse, yazarlar beklemek zorunda kalır — RCU stall bu durumu rapor eder.

RCU nedir

  Okuyucu:   rcu_read_lock() ... (kısıtlama: uyku yasak, preempt dengeli) ... rcu_read_unlock()
  Güncelleyici: rcu_assign_pointer() ... synchronize_rcu() ... kfree(eski_nesne)
                                              ^
                                              Tüm okuyucular bitene kadar bekle (grace period)
                                              Bu bekleme çok uzun sürerse: RCU stall
    

Konfigürasyon

Kconfig
CONFIG_RCU_STALL_COMMON=y
CONFIG_RCU_CPU_STALL_TIMEOUT=21    # 21 saniye (varsayılan)
CONFIG_RCU_CPU_STALL_SUPPRESS=0    # 0 = uyarıları göster
bash — çalışma zamanı
# RCU stall timeout
cat /sys/module/rcupdate/parameters/rcu_cpu_stall_timeout
# 21

# Timeout'u değiştir
echo 60 > /sys/module/rcupdate/parameters/rcu_cpu_stall_timeout

RCU stall uyarısı

dmesg — RCU stall
[ 1234.567] rcu: INFO: rcu_sched self-detected stall on CPU
[ 1234.568] rcu:     0-....: (5252 ticks this GP) idle=0002/1/0 softirq=1234/1234 fqs=5
[ 1234.568] rcu:     (t=5252 jiffies g=12345 q=12345 ncpus=4)
[ 1234.569] rcu: rcu_sched kthread starved for 5253 jiffies!
[ 1234.570] Task dump for CPU 0:
[ 1234.571] task:my_driver      state:R  running task
[ 1234.572] Call Trace:
[ 1234.573]  my_driver_rcu_cb+0x30/0x60 [my_driver]  ← uzun RCU callback

Yaygın RCU stall nedenleri

rcu_read_lock() içinde uyku
RCU okuma bölümünde msleep(), schedule() veya kilit alma. Preemptible RCU olmadıkça yasak.
Uzun interrupt devre dışı bırakma
IRQ disabled durumdayken grace period ilerleyemez.
Sonsuz döngü
RCU read-side içinde sonsuz döngü — grace period asla bitmez.
CPU yükü
Çok yüksek CPU yükünde RCU kthread'i CPU alamayabilir.

06 lockstat — kilit çekişme istatistikleri

lockstat, her kilit için bekleme süresini, edinim sayısını ve çekişme (contention) yüzdesini istatistiksel olarak takip eder. Performans darboğazı olan kilitleri bulmak için kullanılır.

Konfigürasyon ve etkinleştirme

bash
# lockstat etkinleştir (CONFIG_LOCK_STAT=y gerektirir)
echo 1 > /proc/sys/kernel/lock_stat

# ... yük oluştur / test çalıştır ...

# istatistikleri oku
cat /proc/lock_stat

lock_stat çıktısı

/proc/lock_stat çıktısı
lock_stat version 0.4
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
                              class name    con-bounces    contentions   waittime-min   waittime-max waittime-total   waittime-avg    acq-bounces   acquisitions   holdtime-min
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
                          &my_driver_lock:             0              0               0               0              0               0            145         12458            0.14
                        acquisitions:       12458     holdtime-total:   123456.78 ns    holdtime-avg:    9.91 ns
                   contentions:              0     waittime-total:        0.00 ns     waittime-avg:    0.00 ns

Sütun açıklamaları

con-bounces
Çekişme sırasında kilit başka CPU'ya geçiş sayısı (cache coherency overhead göstergesi).
contentions
Kilit alınamadığı (meşgul) durum sayısı. Yüksekse yoğun rekabet var demektir.
waittime-max
Kilit için en uzun bekleme süresi (ns). Latency spike'larını gösterir.
acquisitions
Toplam kilit edinim sayısı.
holdtime-avg
Ortalama kilit tutma süresi. Çok uzunsa kritik bölge daraltılmalı.
bash — sıralama ve filtreleme
# İstatistikleri sıfırla
echo 0 > /proc/lock_stat
echo 1 > /proc/lock_stat  # yeniden etkinleştir

# En yüksek bekleme süreli kilitler
cat /proc/lock_stat | sort -k8 -rn | head -20

# lockstat devre dışı bırak
echo 0 > /proc/lock_stat

07 Pratik: deadlock senaryo analizi

Gerçek dünya senaryosu: iki kaynak kilidi (bus_lock ve device_lock) iki farklı kod yolunda ters sırada alınıyor. lockdep bu durumu tespit edecek ve rapor üretecek.

Deadlock modülü

deadlock_demo.c
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/kthread.h>
#include <linux/delay.h>

static DEFINE_MUTEX(bus_lock);
static DEFINE_MUTEX(device_lock);

/* Thread A: bus_lock → device_lock sırasıyla alır */
static int thread_a_func(void *data)
{
    pr_info("Thread A: bus_lock alınıyor...\n");
    mutex_lock(&bus_lock);
    pr_info("Thread A: bus_lock alındı. 100ms bekleniyor...\n");
    msleep(100);  /* Thread B'nin device_lock almasına fırsat ver */

    pr_info("Thread A: device_lock alınıyor...\n");
    mutex_lock(&device_lock);  /* lockdep BURAYI yakalar */
    pr_info("Thread A: her iki kilit alındı (bu satıra ulaşılamaz)\n");

    mutex_unlock(&device_lock);
    mutex_unlock(&bus_lock);
    return 0;
}

/* Thread B: device_lock → bus_lock sırasıyla alır (ters sıra!) */
static int thread_b_func(void *data)
{
    msleep(50);  /* Thread A'nın bus_lock almasına fırsat ver */

    pr_info("Thread B: device_lock alınıyor...\n");
    mutex_lock(&device_lock);
    pr_info("Thread B: device_lock alındı. bus_lock alınıyor...\n");
    mutex_lock(&bus_lock);     /* lockdep BURAYI yakalar: AB-BA sırası */
    pr_info("Thread B: her iki kilit alındı (bu satıra ulaşılamaz)\n");

    mutex_unlock(&bus_lock);
    mutex_unlock(&device_lock);
    return 0;
}

static struct task_struct *ta, *tb;

static int __init deadlock_init(void)
{
    pr_info("deadlock_demo başlatıldı\n");
    ta = kthread_run(thread_a_func, NULL, "deadlock-a");
    tb = kthread_run(thread_b_func, NULL, "deadlock-b");
    return 0;
}

static void __exit deadlock_exit(void)
{
    /* Gerçekte deadlock oluşacağından bu kısma ulaşılamaz */
    pr_info("deadlock_demo kaldırıldı\n");
}

module_init(deadlock_init);
module_exit(deadlock_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("lockdep deadlock tespiti demo");

Modülü yükle ve lockdep raporunu gözlemle

bash
insmod deadlock_demo.ko
dmesg | grep -A 60 'circular locking'
dmesg — beklenen lockdep raporu
======================================================
WARNING: possible circular locking dependency detected
------------------------------------------------------
deadlock-b/1235 is trying to acquire lock:
ffffffff82345678 (bus_lock){+.+.}-{3:3}, at: thread_b_func+0x5c/0x80

but task is already holding lock:
ffffffff82345690 (device_lock){+.+.}-{3:3}, at: thread_b_func+0x30/0x80

which lock already depends on the new lock.

Possible unsafe locking scenario:
      CPU0 (deadlock-a)        CPU1 (deadlock-b)
      -----------------        -----------------
 lock(bus_lock);          lock(device_lock);
                           lock(bus_lock);     ← burada bekler
 lock(device_lock);  ← burada bekler
           ↑↑↑ DEADLOCK ↑↑↑

Call Trace:
 thread_b_func+0x5c/0x80 [deadlock_demo]
======================================================

Çözüm — sabit kilit sırası

C — düzeltilmiş thread_b_func
/* KURAL: Her zaman bus_lock → device_lock sırasıyla al */
static int thread_b_func_fixed(void *data)
{
    msleep(50);

    /* bus_lock her zaman ÖNCE alınır — tüm kod yollarında tutarlı sıra */
    mutex_lock(&bus_lock);
    mutex_lock(&device_lock);

    /* kritik bölge */

    mutex_unlock(&device_lock);
    mutex_unlock(&bus_lock);
    return 0;
}

lockdep ile test doğrulama

bash
# Düzeltilmiş modülü yükle
insmod deadlock_fixed.ko
sleep 1
dmesg | grep -c 'circular locking'
# 0  ← lockdep uyarısı yok → kilit sırası tutarlı

Hatırlanacaklar

  • lockdep, deadlock olmadan önce kilit sırası döngüsünü tespit eder — CONFIG_PROVE_LOCKING=y
  • Rapordaki "Possible unsafe locking scenario" bölümü, hangi CPU'nun hangi sırayla kilit aldığını gösterir
  • IRQ bağlamında mutex kullanmak BUG: sleeping function called from invalid context üretir
  • Hung task = state:D + uzun süre — deadlock, I/O timeout veya kernel bug
  • echo 60 > /proc/sys/kernel/hung_task_timeout_secs — gömülü sistemler için timeout ayarı
  • RCU stall = grace period bitmeden okuyucu çok uzun süre meşgul — rcu_read_lock() içinde uyku yasak
  • lockstat: /proc/lock_stat — kilit başına bekleme süresi ve çekişme istatistikleri

Kernel debug rehberlerinin tümüne dön: Kernel Debug Araçları katalogu.