Linux ji bo debuggkirina kernel û sepanan gelek amûr hene. Piraniya wan bandorek neyînî li ser performansa serîlêdanê dikin û nekarin di hilberînê de werin bikar anîn.
Çend sal berê hebû
Jixwe gelek karûbarên serîlêdanê hene ku eBPF bikar tînin, û di vê gotarê de em ê binihêrin ka meriv çawa li ser bingeha pirtûkxaneyê karûbarê profîla xwe binivîsîne.
Ceph Hêdî ye
Mêvandarek nû li koma Ceph hate zêdekirin. Piştî koçkirina hin daneyan li ser wê, me dît ku leza hilanîna daxwazên nivîsandinê ji hêla wê ve ji serverên din pir kêmtir bû.
Berevajî platformên din, vê mêvandarê bcache û kernelê nû linux 4.15 bikar anî. Ev cara yekem bû ku mêvandarek vê veavakirinê li vir hate bikar anîn. Û di wê gavê de diyar bû ku koka pirsgirêkê bi teorîk dikare her tişt be.
Lêkolîna Host
Ka em bi nihêrîna ka di hundurê pêvajoya ceph-osd de çi diqewime dest pê bikin. Ji bo vê em ê bikar bînin
Wêne ji me re dibêje ku fonksiyonê fdatasync() gelek dem derbas kir ku daxwazek ji fonksiyonan re bişîne generic_make_request(). Ev tê vê wateyê ku bi îhtîmalek mezin sedema pirsgirêkan li derûdora osd daemon bixwe ye. Ev dikare bibe kernel an dîsk. Derketina iostat di pêvajoyên daxwazên ji hêla dîskên bcache de derengiyek bilind nîşan da.
Dema ku mêvandar kontrol kirin, me dît ku daemon systemd-udevd gelek wextê CPU-ê dixwe - li ser çend naverokan% 20. Ev tevgerek xerîb e, ji ber vê yekê hûn hewce ne ku hûn bizanin ka çima. Ji ber ku Systemd-udevd bi uevents re dixebite, me biryar da ku em li wan binihêrin çavdêriya udevadm. Derket holê ku ji bo her amûrek blokê di pergalê de hejmareke mezin ji bûyerên guherînê hatine çêkirin. Ev pir ne asayî ye, ji ber vê yekê em ê neçar bin ku li van bûyeran çi diafirîne binihêrin.
Bikaranîna BCC Toolkit
Wekî ku me berê jî fêr kir, kernel (û ceph daemon di banga pergalê de) gelek wext tê de derbas dike generic_make_request(). Ka em hewl bidin ku leza vê fonksiyonê bipîvin. LI
Ev taybetmendî bi gelemperî zû dixebite. Tiştê ku dike ev e ku daxwazê ji rêza ajokera cîhazê re derbas bike.
Bcache amûrek tevlihev e ku bi rastî ji sê dîskan pêk tê:
- cîhaza piştgiriyê (dîska cached), di vê rewşê de ew HDD hêdî ye;
- cîhaza caching (dîska caching), li vir ev yek dabeşkirina cîhaza NVMe ye;
- cîhaza virtual bcache ya ku serîlêdan pê dimeşe.
Em dizanin ku veguheztina daxwazê hêdî ye, lê ji bo kîjan ji van amûran? Em ê hinekî paşê bi vê yekê re mijûl bibin.
Naha em dizanin ku bûyer dibe sedema pirsgirêkan. Dîtina ku tam dibe sedema nifşa wan ne ew çend hêsan e. Ka em bifikirin ku ev celebek nermalava ku bi periyodîk tê destpêkirin e. Ka em bibînin ka çi celeb nermalava li ser pergalê bi karanîna skrîptê dimeşîne execsnoop ji heman
Mînak bi vî rengî:
/usr/share/bcc/tools/execsnoop | tee ./execdump
Em ê li vir hilberîna tevahî ya execsnoop nîşan nedin, lê yek rêzek balkêş ji me re wiha xuya bû:
sh 1764905 5802 0 sudo arcconf getconfig 1 AD | grep Temperature | awk -F '[:/]' '{print $2}' | sed 's/^ ([0-9]*) C.*/1/'
Stûna sêyemîn PPID (PID dêûbav) ya pêvajoyê ye. Pêvajoya bi PID 5802 ve derket ku yek ji mijarên pergala çavdêriya me ye. Dema ku veavakirina pergala çavdêriyê kontrol kirin, pîvanên xelet hatin dîtin. Germahiya adapterê HBA her 30 saniyeyan carekê hate girtin, ku pir caran ji hewcedariyê pirtir e. Piştî guheztina navbera kontrolê ya dirêjtir, me dît ku derengiya pêvajoya daxwaznameyê li ser vê mêvandarê nema li gorî mêvandarên din xuya dike.
Lê hîn jî ne diyar e çima cîhaza bcache ew qas hêdî bû. Me platformek ceribandinê ya bi veavakirinek wekhev amade kir û hewl da ku pirsgirêkê bi xebitandina fio li ser bcache-ê, bi periyodîk vekêşana udevadm-ê bixebitînin da ku uevents biafirînin.
Nivîsandina Amûrên Bingeha BCC
Ka em hewl bidin ku amûrek hêsan binivîsin da ku bangên herî hêdî bişopînin û nîşan bidin generic_make_request(). Di heman demê de em bi navê ajokera ku ji bo vê fonksiyonê hatî gazî kirin jî eleqedar dibin.
Plana hêsan e:
- Fêhrist kprobe li ser generic_make_request():
- Em navê dîskê di bîranînê de hilînin, ku bi argumana fonksiyonê ve tê gihîştin;
- Em demjimêrê xilas dikin.
- Fêhrist kretprobe ji bo vegerê ji generic_make_request():
- Em demjimêra heyî distînin;
- Em li zemaneya tomarkirî digerin û wê bi ya niha re berhev dikin;
- Ger encam ji ya diyarkirî mezintir be, wê hingê em navê dîska tomarkirî dibînin û li ser termînalê nîşan didin.
Kprobes и kretprobes mekanîzmayek xala veqetînê bikar bînin da ku koda fonksiyonê li ser firînê biguhezînin. Hûn dikarin bixwînin
Nivîsara eBPF di hundurê nivîsara python de wiha xuya dike:
bpf_text = “”” # Here will be the bpf program code “””
Ji bo danûstandina daneyan di navbera fonksiyonan de, bernameyên eBPF bikar tînin
struct data_t {
u64 pid;
u64 ts;
char comm[TASK_COMM_LEN];
u64 lat;
char disk[DISK_NAME_LEN];
};
BPF_HASH(p, u64, struct data_t);
BPF_PERF_OUTPUT(events);
Li vir em tabloyek hash a bi navê tomar dikin p, bi cureyê key u64 û nirxek celeb struct data_t. Tablo dê di çarçoveya bernameya meya BPF de peyda bibe. BPF_PERF_OUTPUT makro tabloyek din a ku jê re tê gotin tomar dike bûyerên, ku ji bo tê bikaranîn
Dema ku dereng di navbera bangkirina fonksiyonek û vegerandina jê, an jî di navbera bangên fonksiyonên cihêreng de pîvandin, hûn hewce ne ku zanibin ku daneyên wergirtî divê di heman çarçoveyê de bin. Bi gotinek din, hûn hewce ne ku di derbarê destpêkirina paralel a gengaz a fonksiyonan de bîr bînin. Hêza me heye ku em derengiya di navbera bangkirina fonksiyonek di çarçoveyek pêvajoyek de û vegerandina ji wê fonksiyonê di çarçoweya pêvajoyek din de bipîvin, lê ev îhtîmal bêkêr e. Li vir mînakek baş dê bibe
Dûv re, pêdivî ye ku em kodê binivîsin ku dê dema ku fonksiyona di bin lêkolînê de tê gotin were xebitandin:
void start(struct pt_regs *ctx, struct bio *bio) {
u64 pid = bpf_get_current_pid_tgid();
struct data_t data = {};
u64 ts = bpf_ktime_get_ns();
data.pid = pid;
data.ts = ts;
bpf_probe_read_str(&data.disk, sizeof(data.disk), (void*)bio->bi_disk->disk_name);
p.update(&pid, &data);
}
Li vir argûmana yekem a fonksiyona tê gotin dê wekî argumana duyemîn were veguheztin
Fonksiyona jêrîn dê di vegerê de were gazî kirin generic_make_request():
void stop(struct pt_regs *ctx) {
u64 pid = bpf_get_current_pid_tgid();
u64 ts = bpf_ktime_get_ns();
struct data_t* data = p.lookup(&pid);
if (data != 0 && data->ts > 0) {
bpf_get_current_comm(&data->comm, sizeof(data->comm));
data->lat = (ts - data->ts)/1000;
if (data->lat > MIN_US) {
FACTOR
data->pid >>= 32;
events.perf_submit(ctx, data, sizeof(struct data_t));
}
p.delete(&pid);
}
}
Ev fonksiyon mîna ya berê ye: em PID-a pêvajoyê û nîşana demê fêr dibin, lê ji bo avahiya daneya nû bîranîn venaqetînin. Di şûna wê de, em bi karanîna mifteya == PID-a heyî li tabloya haş li avahiyek berê ya heyî digerin. Ger avahî were dîtin, wê hingê em navê pêvajoya xebitandinê fêr dibin û lê zêde dikin.
Veguheztina binaryê ku em li vir bikar tînin, ji bo bidestxistina GID-a mijarê hewce ye. ewan. PID ya pêvajoya sereke ya ku di çarçoveya ku em dixebitin de dest pê kir. Fonksiyona ku em jê re dibêjin
Dema ku em li termînalê derdixin, em niha eleqedar nabin mijarê, lê em bi pêvajoya sereke re eleqedar in. Piştî ku derengiya encam bi bendek diyarkirî re berhev bikin, em avahiya xwe derbas dikin jimare nav cîhê bikarhêner bi riya tabloyê bûyerên, piştî ku em têketinê jêbirin p.
Di skrîpta python a ku dê vê kodê bar bike, pêdivî ye ku em MIN_US û FACTOR bi tixûbên derengmayînê û yekîneyên demê re biguhezînin, ku em ê di nav argumanan re derbas bikin:
bpf_text = bpf_text.replace('MIN_US',str(min_usec))
if args.milliseconds:
bpf_text = bpf_text.replace('FACTOR','data->lat /= 1000;')
label = "msec"
else:
bpf_text = bpf_text.replace('FACTOR','')
label = "usec"
Naha divê em bernameya BPF bi rê ve amade bikin
b = BPF(text=bpf_text)
b.attach_kprobe(event="generic_make_request",fn_name="start")
b.attach_kretprobe(event="generic_make_request",fn_name="stop")
Em ê jî diyar bikin struct data_t di nivîsara me de, wekî din em ê nikaribin tiştek bixwînin:
TASK_COMM_LEN = 16 # linux/sched.h
DISK_NAME_LEN = 32 # linux/genhd.h
class Data(ct.Structure):
_fields_ = [("pid", ct.c_ulonglong),
("ts", ct.c_ulonglong),
("comm", ct.c_char * TASK_COMM_LEN),
("lat", ct.c_ulonglong),
("disk",ct.c_char * DISK_NAME_LEN)]
Pêngava paşîn ev e ku meriv daneya li termînalê derxe:
def print_event(cpu, data, size):
global start
event = ct.cast(data, ct.POINTER(Data)).contents
if start == 0:
start = event.ts
time_s = (float(event.ts - start)) / 1000000000
print("%-18.9f %-16s %-6d %-1s %s %s" % (time_s, event.comm, event.pid, event.lat, label, event.disk))
b["events"].open_perf_buffer(print_event)
# format output
start = 0
while 1:
try:
b.perf_buffer_poll()
except KeyboardInterrupt:
exit()
Skrîpt bi xwe li ser heye
Paşan! Naha em dibînin ku ya ku dişibihe amûrek bcache ya rawestandî bi rastî bangek sekinî ye generic_make_request() ji bo dîskek cached.
Di Kernelê de bikolin
Di dema veguheztina daxwazê de bi rastî çi hêdî dibe? Em dibînin ku dereng berî destpêkirina hesabkirina daxwazê jî çêdibe, yanî. hesabkirina daxwazek taybetî ya ji bo hilberîna bêtir statîstîkên li ser wê (/proc/diskstats an iostat) hîn dest pê nekiriye. Ev dikare bi hêsanî verastkirina iostat dema ku pirsgirêk ji nû ve hilberandin, an
Ger em li fonksiyonê binêrin generic_make_request(), wê hingê em ê bibînin ku berî ku daxwaz dest bi hesabkirinê bike, du fonksiyonên din têne gazî kirin. Yekem - generic_make_request_checks(), di derheqê mîhengên dîskê de li ser rewabûna daxwazê kontrol dike. Duyem -
ret = wait_event_interruptible(q->mq_freeze_wq,
(atomic_read(&q->mq_freeze_depth) == 0 &&
(preempt || !blk_queue_preempt_only(q))) ||
blk_queue_dying(q));
Di wê de, kernel li bendê ye ku rêz bê cemidandin. Ka em derengiyê bipîvin blk_queue_enter():
~# /usr/share/bcc/tools/funclatency blk_queue_enter -i 1 -m
Tracing 1 functions for "blk_queue_enter"... Hit Ctrl-C to end.
msecs : count distribution
0 -> 1 : 341 |****************************************|
msecs : count distribution
0 -> 1 : 316 |****************************************|
msecs : count distribution
0 -> 1 : 255 |****************************************|
2 -> 3 : 0 | |
4 -> 7 : 0 | |
8 -> 15 : 1 | |
Wusa dixuye ku em nêzî çareseriyê ne. Fonksiyonên ku ji bo cemidandin/necemidandina dorê têne bikar anîn ev in
Wextê ku ji bo paqijkirina vê rêzê digire bi derengiya dîskê re wekhev e ji ber ku kernel li bendê ye ku hemî operasyonên rêzê biqede. Dema ku rêz vala bibe, guheztinên mîhengan têne sepandin. Piştî ku tê gotin
Niha em têra xwe dizanin ku rewşê rast bikin. Fermana tetikê ya udevadm dibe sedem ku mîhengên ji bo cîhaza blokê were sepandin. Van mîhengan di qaîdeyên udev de têne diyar kirin. Em dikarin bibînin ka kîjan mîhengan rêzê dicemidîne bi hewldana guhertina wan bi navgîniya sysfs an jî bi dîtina koda çavkaniya kernelê. Em dikarin karûbarê BCC jî biceribînin
~# /usr/share/bcc/tools/trace blk_freeze_queue -K -U
PID TID COMM FUNC
3809642 3809642 systemd-udevd blk_freeze_queue
blk_freeze_queue+0x1 [kernel]
elevator_switch+0x29 [kernel]
elv_iosched_store+0x197 [kernel]
queue_attr_store+0x5c [kernel]
sysfs_kf_write+0x3c [kernel]
kernfs_fop_write+0x125 [kernel]
__vfs_write+0x1b [kernel]
vfs_write+0xb8 [kernel]
sys_write+0x55 [kernel]
do_syscall_64+0x73 [kernel]
entry_SYSCALL_64_after_hwframe+0x3d [kernel]
__write_nocancel+0x7 [libc-2.23.so]
[unknown]
3809631 3809631 systemd-udevd blk_freeze_queue
blk_freeze_queue+0x1 [kernel]
queue_requests_store+0xb6 [kernel]
queue_attr_store+0x5c [kernel]
sysfs_kf_write+0x3c [kernel]
kernfs_fop_write+0x125 [kernel]
__vfs_write+0x1b [kernel]
vfs_write+0xb8 [kernel]
sys_write+0x55 [kernel]
do_syscall_64+0x73 [kernel]
entry_SYSCALL_64_after_hwframe+0x3d [kernel]
__write_nocancel+0x7 [libc-2.23.so]
[unknown]
Qaîdeyên Udev pir kêm kêm diguhezin û bi gelemperî ev bi rengek kontrolkirî diqewime. Ji ber vê yekê em dibînin ku tewra sepandina nirxên jixwe hatî destnîşankirî jî dibe sedema derengiya veguhestina daxwazê ji serîlêdanê li dîskê. Bê guman, çêkirina bûyerên udev dema ku di veavakirina dîskê de guheztin çênebin (mînak, cîhaz ne siwar kirin/veqetandin) ne pratîkek baş e. Lêbelê, em dikarin alîkariya kernel bikin ku karên nehewce neke û rêza daxwaznameyê bicemidîne ger ne hewce be.
Xelasî
eBPF amûrek pir maqûl û bi hêz e. Di gotarê de me li dozek pratîkî nihêrî û beşek piçûk a ku dikare were kirin destnîşan kir. Ger hûn bala xwe didin pêşdebirina karûbarên BCC, hêja ye ku meriv lê binêre
Li ser bingeha eBPF-ê amûrên debugkirin û profîlên din ên balkêş hene. Yek ji wan -
Source: www.habr.com