Linux Kernel'daki eBPF Spinlock Sorunları ve Çözümü

Linux Kernel'daki eBPF Spinlock Sorunları ve Çözümü

Linux kernel'inde eBPF spinlock sorunları, Superluminal ile yapılan testlerde ortaya çıktı. Bu makalede çözüm sürecini inceliyoruz.

Paylas

Giriş

Superluminal, CPU profilleme aracı olarak Linux versiyonu üzerinde çalışırken, bir testerımızın sistemi sık sık dondurması sorunu ile karşılaştık. Sorunun çözümü, Linux kernel'inin derinliklerine inmemizi sağladı. Bu süreçte eBPF (Extended Berkeley Packet Filter) ve kernel spinlock mekanizmalarının karmaşıklığını öğrenme fırsatımız oldu.

Sorunun Başlangıcı

Fedora 42 işletim sistemi (kernel 6.17.4-200) kullanan testerımız, Superluminal ile analiz yaparken sistemin periyodik olarak dondurulmasıyla karşılaştı. İlk olarak, sanal makinelerde sorunu tekrar üretmeye çalıştık ancak başarısız olduk. Fiziksel bir makineye geçtiğimizde ise sorunu tekrarlayabildik.

İlk İnceleme

Superluminal ile yapılan analizde, her bir iş parçacığının zaman çizelgesinde şüpheli alanlar tespit edildi. Bu alanlar, iş parçacıklarının aynı süre boyunca meşgul olduğunu gösteriyordu, bu da beklenen iş yüküyle uyuşmuyordu. dmesg çıktısında ise NMI (Non-Maskable Interrupt) yöneticisinin uzun süre çalıştığını gösteren mesajlar yer aldı. Bu durum, kernel içinde bir şeylerin 250 milisaniye kadar sürdüğünü gösteriyor, fakat nedenini anlamak için daha fazla bilgiye ihtiyaç vardı.

Kernel Debugging

Kernel donmalarıyla karşılaştığımızda, normal bir uygulama donmasında olduğu gibi basit bir debugger ile durumu incelemek mümkün olmuyor. Kernel debugging için özel bir makineye ve serial porta ihtiyaç duyuluyor. Eski makinelerde bulunan COM portları modern donanımlarda yer almıyor. Bu nedenle, PCIe kartları alarak bu bağlantıyı sağladık. Ancak, kernel donduğunda debugger'ımız da yanıt vermiyordu. Bu durum, hata ayıklamayı zorlaştırıyordu.

Minimal Reproduksiyon Bulma

Sorunun kaynağını bulmak için, eBPF kodumuzu gözden geçirmeye karar verdik. eBPF kodu 2000 satırdan oluşuyordu ve sorun bu alanda olabileceğini düşündük. Farklı olay türlerini tek tek devre dışı bırakarak denemeler yaptık. Sonuçlar şöyleydi:

  • Yalnızca örnekleme olayları etkin olduğunda donmalar yaşanmadı.

  • Yalnızca bağlam değişimi/wake olayları etkin olduğunda donmalar yaşanmadı.

  • Her iki olay türü etkin olduğunda donmalar tekrar meydana geldi.

Bu durum, eBPF kodunun örnekleme ve bağlam değişimi olayları arasında bir etkileşim yaşadığını gösteriyor. Örnekleme frekansını düşürdüğümüzde donmaların sıklığı azalmıştı. Sonunda, donmaları durdurmayı başardığımız minimal eBPF reproduksiyon kodunu bulduk:

struct {
 __uint(type, BPF_MAP_TYPE_RINGBUF);
 __uint(max_entries, 512 * 1024 * 1024);
} ringBuffer SEC(".maps");

SEC("tp_btf/sched_switch")
int cswitch(struct bpf_raw_tracepoint_args* inContext) {
 struct CSwitchEvent* event = bpf_ringbuf_reserve(&ringBuffer, sizeof(struct CSwitchEvent), 0);
 if (event == NULL) return 1;

 bpf_ringbuf_discard(event, 0);
 return 0;
}

SEC("perf_event")
int sample(struct bpf_perf_event_data* inContext) {
 struct SampleEvent* event = bpf_ringbuf_reserve(&ringBuffer, sizeof(struct SampleEvent), 0);
 if (event == NULL) return 1;

 bpf_ringbuf_discard(event, 0);
 return 0;
}

Bu kod, bir bağlam değişimi ve bir örnekleme kesintisi olduğunda çalışan iki eBPF programını tanımlıyor. Bu programlar, bir ring buffer'da alan ayırmaktan başka bir iş yapmıyor. Bu sorunun çözüm süreci, Linux kernel'inin karmaşıklığında derinleşmemizi sağladı.

Şevval Yüce

Yazar

Şevval Yüce

TechPusula yazarı. Teknoloji ve dijital dönüşüm üzerine içerikler üretmektedir.

Tüm yazıları gör

Yorumlar

Henüz yorum yapılmamış. İlk yorumu siz yapın!

Yorum Yaz

0/2000

İlginizi Çekebilir

Tüm yazılar