ΠžΡ‚ High Ceph Latency Π΄ΠΎ Kernel Patch с ΠΏΠΎΠΌΠΎΡ‰Ρ‚Π° Π½Π° eBPF/BCC

ΠžΡ‚ High Ceph Latency Π΄ΠΎ Kernel Patch с ΠΏΠΎΠΌΠΎΡ‰Ρ‚Π° Π½Π° eBPF/BCC

Linux ΠΈΠΌΠ° голям Π±Ρ€ΠΎΠΉ инструмСнти Π·Π° отстраняванС Π½Π° Π³Ρ€Π΅ΡˆΠΊΠΈ Π² ядрото ΠΈ прилоТСнията. ΠŸΠΎΠ²Π΅Ρ‡Π΅Ρ‚ΠΎ ΠΎΡ‚ тях ΠΈΠΌΠ°Ρ‚ ΠΎΡ‚Ρ€ΠΈΡ†Π°Ρ‚Π΅Π»Π½ΠΎ Π²ΡŠΠ·Π΄Π΅ΠΉΡΡ‚Π²ΠΈΠ΅ Π²ΡŠΡ€Ρ…Ρƒ производитСлността Π½Π° ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅Ρ‚ΠΎ ΠΈ Π½Π΅ ΠΌΠΎΠ³Π°Ρ‚ Π΄Π° сС ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Ρ‚ Π² производството.

ΠŸΡ€Π΅Π΄ΠΈ няколко Π³ΠΎΠ΄ΠΈΠ½ΠΈ имашС Π΅ Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Π΅Π½ Π΄Ρ€ΡƒΠ³ инструмСнт β€” eBPF. Π’ΠΎΠ²Π° ΠΏΡ€Π°Π²ΠΈ възмоТно прослСдяванСто Π½Π° ядрото ΠΈ потрСбитСлскитС прилоТСния с ниски Ρ€Π°Π·Ρ…ΠΎΠ΄ΠΈ ΠΈ Π±Π΅Π· нСобходимост ΠΎΡ‚ ΠΏΠΎΠ²Ρ‚ΠΎΡ€Π½ΠΎ ΠΈΠ·Π³Ρ€Π°ΠΆΠ΄Π°Π½Π΅ Π½Π° ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΈ ΠΈ Π·Π°Ρ€Π΅ΠΆΠ΄Π°Π½Π΅ Π½Π° ΠΌΠΎΠ΄ΡƒΠ»ΠΈ Π½Π° Ρ‚Ρ€Π΅Ρ‚ΠΈ страни Π² ядрото.

Π’Π΅Ρ‡Π΅ ΠΈΠΌΠ° ΠΌΠ½ΠΎΠ³ΠΎ ΠΏΠΎΠΌΠΎΡ‰Π½ΠΈ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΈ Π·Π° прилоТСния, ΠΊΠΎΠΈΡ‚ΠΎ ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Ρ‚ eBPF, ΠΈ Π² Ρ‚Π°Π·ΠΈ статия Ρ‰Π΅ Ρ€Π°Π·Π³Π»Π΅Π΄Π°ΠΌΠ΅ ΠΊΠ°ΠΊ Π΄Π° Π½Π°ΠΏΠΈΡˆΠ΅Ρ‚Π΅ своя собствСна ΠΏΠΎΠΌΠΎΡ‰Π½Π° ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠ° Π·Π° ΠΏΡ€ΠΎΡ„ΠΈΠ»ΠΈΡ€Π°Π½Π΅, Π±Π°Π·ΠΈΡ€Π°Π½Π° Π½Π° Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ°Ρ‚Π° PythonBCC. Бтатията Π΅ Π±Π°Π·ΠΈΡ€Π°Π½Π° Π½Π° Ρ€Π΅Π°Π»Π½ΠΈ ΡΡŠΠ±ΠΈΡ‚ΠΈΡ. Π©Π΅ ΠΏΡ€Π΅ΠΌΠΈΠ½Π΅ΠΌ ΠΎΡ‚ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌ към Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅, Π·Π° Π΄Π° ΠΏΠΎΠΊΠ°ΠΆΠ΅ΠΌ ΠΊΠ°ΠΊ ΡΡŠΡ‰Π΅ΡΡ‚Π²ΡƒΠ²Π°Ρ‰ΠΈΡ‚Π΅ ΠΏΠΎΠΌΠΎΡ‰Π½ΠΈ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΈ ΠΌΠΎΠ³Π°Ρ‚ Π΄Π° сС ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Ρ‚ Π² ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½ΠΈ ситуации.

Π¦Π΅Ρ„ Π΅ Π±Π°Π²Π΅Π½

Нов хост Π΅ Π΄ΠΎΠ±Π°Π²Π΅Π½ към ΠΊΠ»ΡŠΡΡ‚Π΅Ρ€Π° Ceph. Π‘Π»Π΅Π΄ ΠΊΠ°Ρ‚ΠΎ ΠΌΠΈΠ³Ρ€ΠΈΡ€Π°Ρ…ΠΌΠ΅ част ΠΎΡ‚ Π΄Π°Π½Π½ΠΈΡ‚Π΅ към Π½Π΅Π³ΠΎ, забСлязахмС, Ρ‡Π΅ скоростта Π½Π° ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° Π½Π° заявкитС Π·Π° запис ΠΎΡ‚ Π½Π΅Π³ΠΎ бСшС ΠΌΠ½ΠΎΠ³ΠΎ ΠΏΠΎ-ниска, ΠΎΡ‚ΠΊΠΎΠ»ΠΊΠΎΡ‚ΠΎ Π½Π° Π΄Ρ€ΡƒΠ³ΠΈ ΡΡŠΡ€Π²ΡŠΡ€ΠΈ.

ΠžΡ‚ High Ceph Latency Π΄ΠΎ Kernel Patch с ΠΏΠΎΠΌΠΎΡ‰Ρ‚Π° Π½Π° eBPF/BCC
Π—Π° Ρ€Π°Π·Π»ΠΈΠΊΠ° ΠΎΡ‚ Π΄Ρ€ΡƒΠ³ΠΈ ΠΏΠ»Π°Ρ‚Ρ„ΠΎΡ€ΠΌΠΈ, Ρ‚ΠΎΠ·ΠΈ хост ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π° bcache ΠΈ Π½ΠΎΠ²ΠΎΡ‚ΠΎ ядро ​​на Linux 4.15. Π’ΠΎΠ²Π° бСшС ΠΏΡŠΡ€Π²ΠΈΡΡ‚ ΠΏΡŠΡ‚, ΠΊΠΎΠ³Π°Ρ‚ΠΎ хост ΠΎΡ‚ Ρ‚Π°Π·ΠΈ конфигурация бСшС ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Π½ Ρ‚ΡƒΠΊ. И Π² Ρ‚ΠΎΠ·ΠΈ ΠΌΠΎΠΌΠ΅Π½Ρ‚ бСшС ясно, Ρ‡Π΅ ΠΊΠΎΡ€Π΅Π½ΡŠΡ‚ Π½Π° ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠ° Ρ‚Π΅ΠΎΡ€Π΅Ρ‚ΠΈΡ‡Π½ΠΎ ΠΌΠΎΠΆΠ΅ Π΄Π° бъдС всичко.

РазслСдванС на домакина

НСка Π·Π°ΠΏΠΎΡ‡Π½Π΅ΠΌ, ΠΊΠ°Ρ‚ΠΎ Ρ€Π°Π·Π³Π»Π΅Π΄Π°ΠΌΠ΅ ΠΊΠ°ΠΊΠ²ΠΎ сС случва Π² процСса ceph-osd. Π—Π° Ρ‚ΠΎΠ²Π° Ρ‰Π΅ ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΌΠ΅ PERF ΠΈ огнСскоп (ΠΏΠΎΠ²Π΅Ρ‡Π΅ Π·Π° ΠΊΠΎΠ΅Ρ‚ΠΎ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ Π΄Π° ΠΏΡ€ΠΎΡ‡Π΅Ρ‚Π΅Ρ‚Π΅ Ρ‚ΡƒΠΊ):

ΠžΡ‚ High Ceph Latency Π΄ΠΎ Kernel Patch с ΠΏΠΎΠΌΠΎΡ‰Ρ‚Π° Π½Π° eBPF/BCC
ΠšΠ°Ρ€Ρ‚ΠΈΠ½Π°Ρ‚Π° Π½ΠΈ ΠΊΠ°Π·Π²Π°, Ρ‡Π΅ функцията fdatasync() ΠΏΡ€Π΅ΠΊΠ°Ρ€Π° ΠΌΠ½ΠΎΠ³ΠΎ Π²Ρ€Π΅ΠΌΠ΅ Π² ΠΈΠ·ΠΏΡ€Π°Ρ‰Π°Π½Π΅ Π½Π° заявка Π΄ΠΎ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ generic_make_request(). Π’ΠΎΠ²Π° ΠΎΠ·Π½Π°Ρ‡Π°Π²Π°, Ρ‡Π΅ Π½Π°ΠΉ-вСроятно ΠΏΡ€ΠΈΡ‡ΠΈΠ½Π°Ρ‚Π° Π·Π° ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠΈΡ‚Π΅ Π΅ някъдС извън самия osd Π΄Π΅ΠΌΠΎΠ½. Π’ΠΎΠ²Π° ΠΌΠΎΠΆΠ΅ Π΄Π° бъдС ΠΈΠ»ΠΈ ядрото, ΠΈΠ»ΠΈ дисковСтС. Π Π΅Π·ΡƒΠ»Ρ‚Π°Ρ‚ΡŠΡ‚ ΠΎΡ‚ iostat ΠΏΠΎΠΊΠ°Π·Π° голямо забавянС ΠΏΡ€ΠΈ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ°Ρ‚Π° Π½Π° заявки ΠΎΡ‚ bcache дисковС.

ΠŸΡ€ΠΈ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° Π½Π° хоста ΠΎΡ‚ΠΊΡ€ΠΈΡ…ΠΌΠ΅, Ρ‡Π΅ Π΄Π΅ΠΌΠΎΠ½ΡŠΡ‚ systemd-udevd консумира голямо количСство процСсорно Π²Ρ€Π΅ΠΌΠ΅ - ΠΎΠΊΠΎΠ»ΠΎ 20% Π½Π° няколко ядра. Π’ΠΎΠ²Π° Π΅ странно ΠΏΠΎΠ²Π΅Π΄Π΅Π½ΠΈΠ΅, Ρ‚Π°ΠΊΠ° Ρ‡Π΅ трябва Π΄Π° Ρ€Π°Π·Π±Π΅Ρ€Π΅Ρ‚Π΅ Π·Π°Ρ‰ΠΎ. Въй ΠΊΠ°Ρ‚ΠΎ Systemd-udevd Ρ€Π°Π±ΠΎΡ‚ΠΈ с uevents, Ρ€Π΅ΡˆΠΈΡ…ΠΌΠ΅ Π΄Π° Π³ΠΈ Ρ€Π°Π·Π³Π»Π΅Π΄Π°ΠΌΠ΅ udevadm ΠΌΠΎΠ½ΠΈΡ‚ΠΎΡ€. Оказва сС, Ρ‡Π΅ голям Π±Ρ€ΠΎΠΉ ΡΡŠΠ±ΠΈΡ‚ΠΈΡ Π·Π° промяна са Π³Π΅Π½Π΅Ρ€ΠΈΡ€Π°Π½ΠΈ Π·Π° всяко Π±Π»ΠΎΠΊΠΎΠ²ΠΎ устройство Π² систСмата. Π’ΠΎΠ²Π° Π΅ доста Π½Π΅ΠΎΠ±ΠΈΡ‡Π°ΠΉΠ½ΠΎ, Ρ‚Π°ΠΊΠ° Ρ‡Π΅ Ρ‰Π΅ трябва Π΄Π° Ρ€Π°Π·Π³Π»Π΅Π΄Π°ΠΌΠ΅ ΠΊΠ°ΠΊΠ²ΠΎ Π³Π΅Π½Π΅Ρ€ΠΈΡ€Π° всички Ρ‚Π΅Π·ΠΈ ΡΡŠΠ±ΠΈΡ‚ΠΈΡ.

ИзползванС на BCC Toolkit

ΠšΠ°ΠΊΡ‚ΠΎ Π²Π΅Ρ‡Π΅ Ρ€Π°Π·Π±Ρ€Π°Ρ…ΠΌΠ΅, ядрото (ΠΈ ceph Π΄Π΅ΠΌΠΎΠ½ΡŠΡ‚ Π² систСмното ΠΈΠ·Π²ΠΈΠΊΠ²Π°Π½Π΅) ΠΏΡ€Π΅ΠΊΠ°Ρ€Π²Π°Ρ‚ ΠΌΠ½ΠΎΠ³ΠΎ Π²Ρ€Π΅ΠΌΠ΅ Π² generic_make_request(). НСка сС ΠΎΠΏΠΈΡ‚Π°ΠΌΠ΅ Π΄Π° ΠΈΠ·ΠΌΠ΅Ρ€ΠΈΠΌ скоростта Π½Π° Ρ‚Π°Π·ΠΈ функция. IN BCC Π’Π΅Ρ‡Π΅ ΠΈΠΌΠ° чудСсна ΠΏΠΎΠΌΠΎΡ‰Π½Π° ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠ° - функционалност. Π©Π΅ прослСдим Π΄Π΅ΠΌΠΎΠ½Π° ΠΏΠΎ нСговия PID с ΠΈΠ½Ρ‚Π΅Ρ€Π²Π°Π» ΠΎΡ‚ 1 сСкунда ΠΌΠ΅ΠΆΠ΄Ρƒ ΠΈΠ·Ρ…ΠΎΠ΄ΠΈΡ‚Π΅ ΠΈ Ρ‰Π΅ ΠΈΠ·Π²Π΅Π΄Π΅ΠΌ Ρ€Π΅Π·ΡƒΠ»Ρ‚Π°Ρ‚Π° Π² милисСкунди.

ΠžΡ‚ High Ceph Latency Π΄ΠΎ Kernel Patch с ΠΏΠΎΠΌΠΎΡ‰Ρ‚Π° Π½Π° eBPF/BCC
Π’Π°Π·ΠΈ функция ΠΎΠ±ΠΈΠΊΠ½ΠΎΠ²Π΅Π½ΠΎ Ρ€Π°Π±ΠΎΡ‚ΠΈ Π±ΡŠΡ€Π·ΠΎ. Всичко, ΠΊΠΎΠ΅Ρ‚ΠΎ ΠΏΡ€Π°Π²ΠΈ, Π΅ Π΄Π° ΠΏΡ€Π΅Ρ…Π²ΡŠΡ€Π»ΠΈ заявката към ΠΎΠΏΠ°ΡˆΠΊΠ°Ρ‚Π° Π½Π° Π΄Ρ€Π°ΠΉΠ²Π΅Ρ€Π° Π½Π° устройството.

Bcache Π΅ слоТно устройство, ΠΊΠΎΠ΅Ρ‚ΠΎ Π²ΡΡŠΡ‰Π½ΠΎΡΡ‚ сС ΡΡŠΡΡ‚ΠΎΠΈ ΠΎΡ‚ Ρ‚Ρ€ΠΈ диска:

  • устройство Π·Π° Π°Ρ€Ρ…ΠΈΠ²ΠΈΡ€Π°Π½Π΅ (ΠΊΠ΅ΡˆΠΈΡ€Π°Π½ диск), Π² Ρ‚ΠΎΠ·ΠΈ случай Ρ‚ΠΎΠ²Π° Π΅ Π±Π°Π²Π΅Π½ HDD;
  • ΠΊΠ΅ΡˆΠΈΡ€Π°Ρ‰ΠΎ устройство (ΠΊΠ΅ΡˆΠΈΡ€Π°Ρ‰ диск), Ρ‚ΡƒΠΊ Ρ‚ΠΎΠ²Π° Π΅ Π΅Π΄ΠΈΠ½ дял Π½Π° NVMe устройството;
  • bcache Π²ΠΈΡ€Ρ‚ΡƒΠ°Π»Π½ΠΎΡ‚ΠΎ устройство, с ΠΊΠΎΠ΅Ρ‚ΠΎ Ρ€Π°Π±ΠΎΡ‚ΠΈ ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅Ρ‚ΠΎ.

Π—Π½Π°Π΅ΠΌ, Ρ‡Π΅ ΠΏΡ€Π΅Π΄Π°Π²Π°Π½Π΅Ρ‚ΠΎ Π½Π° заявка Π΅ Π±Π°Π²Π½ΠΎ, Π½ΠΎ Π·Π° ΠΊΠΎΠ΅ ΠΎΡ‚ Ρ‚Π΅Π·ΠΈ устройства? Π©Π΅ сС Π·Π°Π½ΠΈΠΌΠ°Π΅ΠΌ с Ρ‚ΠΎΠ²Π° ΠΌΠ°Π»ΠΊΠΎ ΠΏΠΎ-късно.

Π‘Π΅Π³Π° Π·Π½Π°Π΅ΠΌ, Ρ‡Π΅ Π΅ вСроятно ΡΡŠΠ±ΠΈΡ‚ΠΈΡΡ‚Π° Π΄Π° причинят ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠΈ. Π”Π° сС ​​установи ΠΊΠ°ΠΊΠ²ΠΎ Ρ‚ΠΎΡ‡Π½ΠΎ причинява тяхното Π³Π΅Π½Π΅Ρ€ΠΈΡ€Π°Π½Π΅ Π½Π΅ Π΅ Ρ‚ΠΎΠ»ΠΊΠΎΠ²Π° лСсно. Π”Π° ΠΏΡ€ΠΈΠ΅ΠΌΠ΅ΠΌ, Ρ‡Π΅ Ρ‚ΠΎΠ²Π° Π΅ някакъв софтуСр, ΠΊΠΎΠΉΡ‚ΠΎ сС стартира ΠΏΠ΅Ρ€ΠΈΠΎΠ΄ΠΈΡ‡Π½ΠΎ. НСка Π΄Π° Π²ΠΈΠ΄ΠΈΠΌ какъв Π²ΠΈΠ΄ софтуСр Ρ€Π°Π±ΠΎΡ‚ΠΈ Π² систСмата с ΠΏΠΎΠΌΠΎΡ‰Ρ‚Π° Π½Π° скрипт execsnoop ΠΎΡ‚ ΡΡŠΡ‰ΠΎΡ‚ΠΎ BCC ΠΏΠΎΠΌΠΎΡ‰Π΅Π½ ΠΊΠΎΠΌΠΏΠ»Π΅ΠΊΡ‚. НСка Π³ΠΎ стартирамС ΠΈ ΠΈΠ·ΠΏΡ€Π°Ρ‚ΠΈΠΌ ΠΈΠ·Ρ…ΠΎΠ΄Π° във Ρ„Π°ΠΉΠ».

НапримСр Ρ‚Π°ΠΊΠ°:

/usr/share/bcc/tools/execsnoop  | tee ./execdump

Няма Π΄Π° ΠΏΠΎΠΊΠ°ΠΆΠ΅ΠΌ пълния Ρ€Π΅Π·ΡƒΠ»Ρ‚Π°Ρ‚ ΠΎΡ‚ execsnoop Ρ‚ΡƒΠΊ, Π½ΠΎ Π΅Π΄ΠΈΠ½ интСрСсСн Π·Π° нас Ρ€Π΅Π΄ изглСТдашС Ρ‚Π°ΠΊΠ°:

sh 1764905 5802 0 sudo arcconf getconfig 1 AD | grep Temperature | awk -F '[:/]' '{print $2}' | sed 's/^ ([0-9]*) C.*/1/'

Π’Ρ€Π΅Ρ‚Π°Ρ‚Π° ΠΊΠΎΠ»ΠΎΠ½Π° Π΅ PPID (родитСлски PID) Π½Π° процСса. ΠŸΡ€ΠΎΡ†Π΅ΡΡŠΡ‚ с PID 5802 сС ΠΎΠΊΠ°Π·Π° Π΅Π΄Π½Π° ΠΎΡ‚ Π½ΠΈΡˆΠΊΠΈΡ‚Π΅ Π½Π° Π½Π°ΡˆΠ°Ρ‚Π° систСма Π·Π° наблюдСниС. ΠŸΡ€ΠΈ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° Π½Π° конфигурацията Π½Π° систСмата Π·Π° ΠΌΠΎΠ½ΠΈΡ‚ΠΎΡ€ΠΈΠ½Π³ бяха ΠΎΡ‚ΠΊΡ€ΠΈΡ‚ΠΈ Π³Ρ€Π΅ΡˆΠ½ΠΈ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΈ. Π’Π΅ΠΌΠΏΠ΅Ρ€Π°Ρ‚ΡƒΡ€Π°Ρ‚Π° Π½Π° HBA Π°Π΄Π°ΠΏΡ‚Π΅Ρ€Π° сС ΠΈΠ·ΠΌΠ΅Ρ€Π²Π° Π½Π° всСки 30 сСкунди, ΠΊΠΎΠ΅Ρ‚ΠΎ Π΅ ΠΌΠ½ΠΎΠ³ΠΎ ΠΏΠΎ-чСсто ΠΎΡ‚ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎΡ‚ΠΎ. Π‘Π»Π΅Π΄ ΠΊΠ°Ρ‚ΠΎ ΠΏΡ€ΠΎΠΌΠ΅Π½ΠΈΡ…ΠΌΠ΅ ΠΈΠ½Ρ‚Π΅Ρ€Π²Π°Π»Π° Π½Π° ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° Π½Π° ΠΏΠΎ-дълъг, ΠΎΡ‚ΠΊΡ€ΠΈΡ…ΠΌΠ΅, Ρ‡Π΅ забавянСто Π½Π° ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ°Ρ‚Π° Π½Π° заявката Π½Π° Ρ‚ΠΎΠ·ΠΈ хост Π²Π΅Ρ‡Π΅ Π½Π΅ сС откроява Π² сравнСниС с Π΄Ρ€ΡƒΠ³ΠΈ хостовС.

Но всС ΠΎΡ‰Π΅ Π½Π΅ Π΅ ясно Π·Π°Ρ‰ΠΎ bcache устройството Π΅ Ρ‚ΠΎΠ»ΠΊΠΎΠ²Π° Π±Π°Π²Π½ΠΎ. ΠŸΠΎΠ΄Π³ΠΎΡ‚Π²ΠΈΡ…ΠΌΠ΅ тСстова ΠΏΠ»Π°Ρ‚Ρ„ΠΎΡ€ΠΌΠ° с ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ‡Π½Π° конфигурация ΠΈ сС ΠΎΠΏΠΈΡ‚Π°Ρ…ΠΌΠ΅ Π΄Π° Π²ΡŠΠ·ΠΏΡ€ΠΎΠΈΠ·Π²Π΅Π΄Π΅ΠΌ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠ°, ΠΊΠ°Ρ‚ΠΎ стартирахмС fio Π½Π° bcache, ΠΏΠ΅Ρ€ΠΈΠΎΠ΄ΠΈΡ‡Π½ΠΎ стартирайки udevadm Ρ‚Ρ€ΠΈΠ³Π΅Ρ€ Π·Π° Π³Π΅Π½Π΅Ρ€ΠΈΡ€Π°Π½Π΅ Π½Π° uevents.

ПисанС Π½Π° инструмСнти, Π±Π°Π·ΠΈΡ€Π°Π½ΠΈ Π½Π° BCC

НСка сС ΠΎΠΏΠΈΡ‚Π°ΠΌΠ΅ Π΄Π° напишСм проста ΠΏΠΎΠΌΠΎΡ‰Π½Π° ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠ° Π·Π° прослСдяванС ΠΈ ΠΏΠΎΠΊΠ°Π·Π²Π°Π½Π΅ Π½Π° Π½Π°ΠΉ-Π±Π°Π²Π½ΠΈΡ‚Π΅ повиквания generic_make_request(). Π˜Π½Ρ‚Π΅Ρ€Π΅ΡΡƒΠ²Π°ΠΌΠ΅ сС ΠΈ ΠΎΡ‚ ΠΈΠΌΠ΅Ρ‚ΠΎ Π½Π° устройството, Π·Π° ΠΊΠΎΠ΅Ρ‚ΠΎ Π΅ ΠΈΠ·Π²ΠΈΠΊΠ°Π½Π° Ρ‚Π°Π·ΠΈ функция.

ΠŸΠ»Π°Π½ΡŠΡ‚ Π΅ прост:

  • РСгистрация kprobe Π½Π° generic_make_request():
    • ЗаписвамС ΠΈΠΌΠ΅Ρ‚ΠΎ Π½Π° диска Π² ΠΏΠ°ΠΌΠ΅Ρ‚Ρ‚Π°, Π΄ΠΎΡΡ‚ΡŠΠΏΠ½ΠΎ Ρ‡Ρ€Π΅Π· Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚Π° Π½Π° функцията;
    • Π—Π°ΠΏΠ°Π·Π²Π°ΠΌΠ΅ ΠΊΠ»Π΅ΠΉΠΌΠΎΡ‚ΠΎ Π·Π° Π²Ρ€Π΅ΠΌΠ΅.

  • РСгистрация kretprobe Π·Π° Π²Ρ€ΡŠΡ‰Π°Π½Π΅ ΠΎΡ‚ generic_make_request():
    • ΠŸΠΎΠ»ΡƒΡ‡Π°Π²Π°ΠΌΠ΅ Ρ‚Π΅ΠΊΡƒΡ‰ΠΎΡ‚ΠΎ Π²Ρ€Π΅ΠΌΠ΅Π²ΠΎ ΠΊΠ»Π΅ΠΉΠΌΠΎ;
    • Π’ΡŠΡ€ΡΠΈΠΌ запазСния timestamp ΠΈ Π³ΠΎ сравнявамС с тСкущия;
    • Ако Ρ€Π΅Π·ΡƒΠ»Ρ‚Π°Ρ‚ΡŠΡ‚ Π΅ ΠΏΠΎ-голям ΠΎΡ‚ посочСния, Π½Π°ΠΌΠΈΡ€Π°ΠΌΠ΅ ΠΈΠΌΠ΅Ρ‚ΠΎ Π½Π° запазСния диск ΠΈ Π³ΠΎ ΠΏΠΎΠΊΠ°Π·Π²Π°ΠΌΠ΅ Π½Π° Ρ‚Π΅Ρ€ΠΌΠΈΠ½Π°Π»Π°.

KΠΏΡ€ΠΎΠ±ΠΈ ΠΈ kretprobes ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΉΡ‚Π΅ ΠΌΠ΅Ρ…Π°Π½ΠΈΠ·ΡŠΠΌ Π·Π° Ρ‚ΠΎΡ‡ΠΊΠ° Π½Π° ΠΏΡ€Π΅ΠΊΡŠΡΠ²Π°Π½Π΅, Π·Π° Π΄Π° ΠΏΡ€ΠΎΠΌΠ΅Π½ΠΈΡ‚Π΅ функционалния ΠΊΠΎΠ΄ Π² Π΄Π²ΠΈΠΆΠ΅Π½ΠΈΠ΅. МоТСш Π΄Π° Ρ‡Π΅Ρ‚Π΅Ρˆ докумСнтация ΠΈ Π΄ΠΎΠ±ΡŠΡ€ статия ΠΏΠΎ Ρ‚Π°Π·ΠΈ Ρ‚Π΅ΠΌΠ°. Ако ΠΏΠΎΠ³Π»Π΅Π΄Π½Π΅Ρ‚Π΅ ΠΊΠΎΠ΄Π° Π½Π° Ρ€Π°Π·Π»ΠΈΡ‡Π½ΠΈ ΠΏΠΎΠΌΠΎΡ‰Π½ΠΈ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΈ Π² BCC, Ρ‚ΠΎΠ³Π°Π²Π° ΠΌΠΎΠΆΠ΅Ρ‚Π΅ Π΄Π° Π²ΠΈΠ΄ΠΈΡ‚Π΅, Ρ‡Π΅ Ρ‚Π΅ ΠΈΠΌΠ°Ρ‚ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ‡Π½Π° структура. Π’Π°ΠΊΠ° Ρ‡Π΅ Π² Ρ‚Π°Π·ΠΈ статия Ρ‰Π΅ пропуснСм Π°Π½Π°Π»ΠΈΠ·ΠΈΡ€Π°Π½Π΅Ρ‚ΠΎ Π½Π° Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚ΠΈΡ‚Π΅ Π½Π° скрипта ΠΈ Ρ‰Π΅ ΠΏΡ€Π΅ΠΌΠΈΠ½Π΅ΠΌ към самата ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠ° BPF.

Π’Π΅ΠΊΡΡ‚ΡŠΡ‚ Π½Π° eBPF Π² скрипта Π½Π° python ΠΈΠ·Π³Π»Π΅ΠΆΠ΄Π° Ρ‚Π°ΠΊΠ°:

bpf_text = β€œβ€β€ # Here will be the bpf program code β€œβ€β€

Π—Π° ΠΎΠ±ΠΌΠ΅Π½ Π½Π° Π΄Π°Π½Π½ΠΈ ΠΌΠ΅ΠΆΠ΄Ρƒ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈΡ‚Π΅ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΈΡ‚Π΅ eBPF ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Ρ‚ Ρ…Π΅Ρˆ Ρ‚Π°Π±Π»ΠΈΡ†ΠΈ. НиС Ρ‰Π΅ Π½Π°ΠΏΡ€Π°Π²ΠΈΠΌ ΡΡŠΡ‰ΠΎΡ‚ΠΎ. Π©Π΅ ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΌΠ΅ PID Π½Π° процСса ΠΊΠ°Ρ‚ΠΎ ΠΊΠ»ΡŽΡ‡ ΠΈ Ρ‰Π΅ Π΄Π΅Ρ„ΠΈΠ½ΠΈΡ€Π°ΠΌΠ΅ структурата ΠΊΠ°Ρ‚ΠΎ стойност:

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);

Π’ΡƒΠΊ рСгистрирамС Ρ…Π΅Ρˆ Ρ‚Π°Π±Π»ΠΈΡ†Π°, Π½Π°Ρ€Π΅Ρ‡Π΅Π½Π° p, с Ρ‚ΠΈΠΏ ΠΊΠ»ΡŽΡ‡ u64 ΠΈ стойност Π½Π° Ρ‚ΠΈΠΏ struct data_t. Π’Π°Π±Π»ΠΈΡ†Π°Ρ‚Π° Ρ‰Π΅ бъдС Π΄ΠΎΡΡ‚ΡŠΠΏΠ½Π° Π² контСкста Π½Π° Π½Π°ΡˆΠ°Ρ‚Π° BPF ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠ°. ΠœΠ°ΠΊΡ€ΠΎΡΡŠΡ‚ BPF_PERF_OUTPUT рСгистрира Π΄Ρ€ΡƒΠ³Π° Ρ‚Π°Π±Π»ΠΈΡ†Π°, ΠΈΠ·Π²ΠΈΠΊΠ°Π½Π° ΡΡŠΠ±ΠΈΡ‚ΠΈΡ, ΠΊΠΎΠΉΡ‚ΠΎ сС ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π° Π·Π° ΠΏΡ€Π΅Π΄Π°Π²Π°Π½Π΅ Π½Π° Π΄Π°Π½Π½ΠΈ Π² потрСбитСлското пространство.

ΠšΠΎΠ³Π°Ρ‚ΠΎ ΠΈΠ·ΠΌΠ΅Ρ€Π²Π°Ρ‚Π΅ Π·Π°ΠΊΡŠΡΠ½Π΅Π½ΠΈΡΡ‚Π° ΠΌΠ΅ΠΆΠ΄Ρƒ ΠΈΠ·Π²ΠΈΠΊΠ²Π°Π½Π΅Ρ‚ΠΎ Π½Π° функция ΠΈ Π²Ρ€ΡŠΡ‰Π°Π½Π΅Ρ‚ΠΎ ΠΎΡ‚ нСя ΠΈΠ»ΠΈ ΠΌΠ΅ΠΆΠ΄Ρƒ извикванията към Ρ€Π°Π·Π»ΠΈΡ‡Π½ΠΈ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ, трябва Π΄Π° Π²Π·Π΅ΠΌΠ΅Ρ‚Π΅ ΠΏΡ€Π΅Π΄Π²ΠΈΠ΄, Ρ‡Π΅ ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½ΠΈΡ‚Π΅ Π΄Π°Π½Π½ΠΈ трябва Π΄Π° ΠΏΡ€ΠΈΠ½Π°Π΄Π»Π΅ΠΆΠ°Ρ‚ към Π΅Π΄ΠΈΠ½ ΠΈ ΡΡŠΡ‰ контСкст. Π‘ Π΄Ρ€ΡƒΠ³ΠΈ Π΄ΡƒΠΌΠΈ, трябва Π΄Π° Π·Π°ΠΏΠΎΠΌΠ½ΠΈΡ‚Π΅ Π·Π° Π²ΡŠΠ·ΠΌΠΎΠΆΠ½ΠΎΡ‚ΠΎ ΠΏΠ°Ρ€Π°Π»Π΅Π»Π½ΠΎ стартиранС Π½Π° Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ. ИмамС способността Π΄Π° ΠΈΠ·ΠΌΠ΅Ρ€Π²Π°ΠΌΠ΅ Π·Π°ΠΊΡŠΡΠ½Π΅Π½ΠΈΠ΅Ρ‚ΠΎ ΠΌΠ΅ΠΆΠ΄Ρƒ ΠΈΠ·Π²ΠΈΠΊΠ²Π°Π½Π΅Ρ‚ΠΎ Π½Π° функция Π² контСкста Π½Π° Π΅Π΄ΠΈΠ½ процСс ΠΈ Π²Ρ€ΡŠΡ‰Π°Π½Π΅Ρ‚ΠΎ ΠΎΡ‚ Ρ‚Π°Π·ΠΈ функция Π² контСкста Π½Π° Π΄Ρ€ΡƒΠ³ процСс, Π½ΠΎ Ρ‚ΠΎΠ²Π° вСроятно Π΅ Π±Π΅Π·ΠΏΠΎΠ»Π΅Π·Π½ΠΎ. Π”ΠΎΠ±ΡŠΡ€ ΠΏΡ€ΠΈΠΌΠ΅Ρ€ Ρ‚ΡƒΠΊ Π±ΠΈ Π±ΠΈΠ» полСзност Π·Π° биолатСнтност, ΠΊΡŠΠ΄Π΅Ρ‚ΠΎ ΠΊΠ»ΡŽΡ‡ΡŠΡ‚ Π½Π° Ρ…Π΅Ρˆ Ρ‚Π°Π±Π»ΠΈΡ†Π°Ρ‚Π° Π΅ Π·Π°Π΄Π°Π΄Π΅Π½ Π½Π° ΡƒΠΊΠ°Π·Π°Ρ‚Π΅Π» към struct заявка, ΠΊΠΎΠ΅Ρ‚ΠΎ отразява Π΅Π΄Π½Π° заявка Π·Π° диск.

Π‘Π»Π΅Π΄ Ρ‚ΠΎΠ²Π° трябва Π΄Π° напишСм ΠΊΠΎΠ΄Π°, ΠΊΠΎΠΉΡ‚ΠΎ Ρ‰Π΅ сС изпълнява, ΠΊΠΎΠ³Π°Ρ‚ΠΎ сС ΠΈΠ·Π²ΠΈΠΊΠ° изслСдваната функция:

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);
}

Π’ΡƒΠΊ ΠΏΡŠΡ€Π²ΠΈΡΡ‚ Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚ Π½Π° ΠΈΠ·Π²ΠΈΠΊΠ°Π½Π°Ρ‚Π° функция Ρ‰Π΅ бъдС Π·Π°ΠΌΠ΅Π½Π΅Π½ ΠΊΠ°Ρ‚ΠΎ Π²Ρ‚ΠΎΡ€ΠΈ Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚ generic_make_request(). Π‘Π»Π΅Π΄ Ρ‚ΠΎΠ²Π° ΠΏΠΎΠ»ΡƒΡ‡Π°Π²Π°ΠΌΠ΅ PID Π½Π° процСса, Π² контСкста Π½Π° ΠΊΠΎΠΉΡ‚ΠΎ Ρ€Π°Π±ΠΎΡ‚ΠΈΠΌ, ΠΈ Ρ‚Π΅ΠΊΡƒΡ‰ΠΎΡ‚ΠΎ Π²Ρ€Π΅ΠΌΠ΅Π²ΠΎ ΠΊΠ»Π΅ΠΉΠΌΠΎ Π² наносСкунди. ЗаписвамС всичко Π² прясно ΠΏΠΎΠ΄Π±Ρ€Π°Π½ struct data_t Π΄Π°Π½Π½ΠΈ. ΠŸΠΎΠ»ΡƒΡ‡Π°Π²Π°ΠΌΠ΅ ΠΈΠΌΠ΅Ρ‚ΠΎ Π½Π° диска ΠΎΡ‚ структурата Π±ΠΈΠΎ, ΠΊΠΎΠΉΡ‚ΠΎ сС ΠΏΡ€Π΅Π΄Π°Π²Π° ΠΏΡ€ΠΈ ΠΈΠ·Π²ΠΈΠΊΠ²Π°Π½Π΅ generic_make_request()ΠΈ Π³ΠΎ Π·Π°ΠΏΠ°Π·Π΅Ρ‚Π΅ Π² ΡΡŠΡ‰Π°Ρ‚Π° структура Π΄Π°Π½Π½ΠΈ. ΠŸΠΎΡΠ»Π΅Π΄Π½Π°Ρ‚Π° ΡΡ‚ΡŠΠΏΠΊΠ° Π΅ Π΄Π° Π΄ΠΎΠ±Π°Π²ΠΈΡ‚Π΅ запис към Ρ…Π΅Ρˆ-Ρ‚Π°Π±Π»ΠΈΡ†Π°Ρ‚Π°, която бСшС спомСната ΠΏΠΎ-Ρ€Π°Π½ΠΎ.

Π‘Π»Π΅Π΄Π½Π°Ρ‚Π° функция Ρ‰Π΅ бъдС ΠΈΠ·Π²ΠΈΠΊΠ°Π½Π° ΠΏΡ€ΠΈ Π²Ρ€ΡŠΡ‰Π°Π½Π΅ ΠΎΡ‚ 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);
    }
}

Π’Π°Π·ΠΈ функция Π΅ ΠΏΠΎΠ΄ΠΎΠ±Π½Π° Π½Π° ΠΏΡ€Π΅Π΄ΠΈΡˆΠ½Π°Ρ‚Π°: ΠΎΡ‚ΠΊΡ€ΠΈΠ²Π°ΠΌΠ΅ PID Π½Π° процСса ΠΈ ΠΊΠ»Π΅ΠΉΠΌΠΎΡ‚ΠΎ Π·Π° Π²Ρ€Π΅ΠΌΠ΅, Π½ΠΎ Π½Π΅ задСлямС ΠΏΠ°ΠΌΠ΅Ρ‚ Π·Π° Π½ΠΎΠ²Π°Ρ‚Π° структура ΠΎΡ‚ Π΄Π°Π½Π½ΠΈ. ВмСсто Ρ‚ΠΎΠ²Π° Ρ‚ΡŠΡ€ΡΠΈΠΌ Π² Ρ…Π΅Ρˆ-Ρ‚Π°Π±Π»ΠΈΡ†Π°Ρ‚Π° Π²Π΅Ρ‡Π΅ ΡΡŠΡ‰Π΅ΡΡ‚Π²ΡƒΠ²Π°Ρ‰Π° структура, ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΉΠΊΠΈ ΠΊΠ»ΡŽΡ‡Π° == тСкущия PID. Ако структурата бъдС Π½Π°ΠΌΠ΅Ρ€Π΅Π½Π°, Ρ‚ΠΎΠ³Π°Π²Π° Π½Π°ΠΌΠΈΡ€Π°ΠΌΠ΅ ΠΈΠΌΠ΅Ρ‚ΠΎ Π½Π° изпълнявания процСс ΠΈ Π³ΠΎ добавямС към Π½Π΅Π³ΠΎ.

Π”Π²ΠΎΠΈΡ‡Π½ΠΎΡ‚ΠΎ измСстванС, ΠΊΠΎΠ΅Ρ‚ΠΎ ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΌΠ΅ Ρ‚ΡƒΠΊ, Π΅ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ, Π·Π° Π΄Π° ΠΏΠΎΠ»ΡƒΡ‡ΠΈΠΌ GID Π½Π° Π½ΠΈΡˆΠΊΠ°Ρ‚Π°. Ρ‚Π΅Π·ΠΈ. PID Π½Π° основния процСс, стартирал Π½ΠΈΡˆΠΊΠ°Ρ‚Π°, Π² контСкста Π½Π° която Ρ€Π°Π±ΠΎΡ‚ΠΈΠΌ. Ѐункцията, която ΠΈΠ·Π²ΠΈΠΊΠ²Π°ΠΌΠ΅ bpf_get_current_pid_tgid() Π²Ρ€ΡŠΡ‰Π° ΠΊΠ°ΠΊΡ‚ΠΎ GID Π½Π° Π½ΠΈΡˆΠΊΠ°Ρ‚Π°, Ρ‚Π°ΠΊΠ° ΠΈ нСйния PID Π² Π΅Π΄Π½Π° 64-Π±ΠΈΡ‚ΠΎΠ²Π° стойност.

ΠšΠΎΠ³Π°Ρ‚ΠΎ ΠΈΠ·Π²Π΅ΠΆΠ΄Π°ΠΌΠ΅ към Ρ‚Π΅Ρ€ΠΌΠΈΠ½Π°Π»Π°, Π² ΠΌΠΎΠΌΠ΅Π½Ρ‚Π° Π½Π΅ сС интСрСсувамС ΠΎΡ‚ Π½ΠΈΡˆΠΊΠ°Ρ‚Π°, Π½ΠΎ сС интСрСсувамС ΠΎΡ‚ главния процСс. Π‘Π»Π΅Π΄ сравняванС Π½Π° ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½ΠΎΡ‚ΠΎ забавянС с Π΄Π°Π΄Π΅Π½ ΠΏΡ€Π°Π³, Π½ΠΈΠ΅ ΠΏΡ€Π΅Π΄Π°Π²Π°ΠΌΠ΅ Π½Π°ΡˆΠ°Ρ‚Π° структура Π΄Π°Π½Π½ΠΈ Π² потрСбитСлското пространство Ρ‡Ρ€Π΅Π· Ρ‚Π°Π±Π»ΠΈΡ†Π° ΡΡŠΠ±ΠΈΡ‚ΠΈΡ, слСд ΠΊΠΎΠ΅Ρ‚ΠΎ ΠΈΠ·Ρ‚Ρ€ΠΈΠ²Π°ΠΌΠ΅ записа ΠΎΡ‚ p.

Π’ скрипта Π½Π° Python, ΠΊΠΎΠΉΡ‚ΠΎ Ρ‰Π΅ Π·Π°Ρ€Π΅Π΄ΠΈ Ρ‚ΠΎΠ·ΠΈ ΠΊΠΎΠ΄, трябва Π΄Π° Π·Π°ΠΌΠ΅Π½ΠΈΠΌ MIN_US ΠΈ FACTOR с ΠΏΡ€Π°Π³ΠΎΠ²Π΅Ρ‚Π΅ Π½Π° забавянС ΠΈ Π²Ρ€Π΅ΠΌΠ΅Π²ΠΈΡ‚Π΅ Π΅Π΄ΠΈΠ½ΠΈΡ†ΠΈ, ΠΊΠΎΠΈΡ‚ΠΎ Ρ‰Π΅ ΠΏΡ€Π΅Ρ…Π²ΡŠΡ€Π»ΠΈΠΌ ΠΏΡ€Π΅Π· Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚ΠΈΡ‚Π΅:

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"

Π‘Π΅Π³Π° трябва Π΄Π° ΠΏΠΎΠ΄Π³ΠΎΡ‚Π²ΠΈΠΌ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠ°Ρ‚Π° Π½Π° BPF Ρ‡Ρ€Π΅Π· BPF макрос ΠΈ рСгистрирайтС ΠΏΡ€ΠΎΠ±ΠΈ:

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")

НиС ΡΡŠΡ‰ΠΎ Ρ‰Π΅ трябва Π΄Π° ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΠΈΠΌ struct data_t Π² нашия скрипт, Π² ΠΏΡ€ΠΎΡ‚ΠΈΠ²Π΅Π½ случай няма Π΄Π° ΠΌΠΎΠΆΠ΅ΠΌ Π΄Π° ΠΏΡ€ΠΎΡ‡Π΅Ρ‚Π΅ΠΌ Π½ΠΈΡ‰ΠΎ:

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)]

ΠŸΠΎΡΠ»Π΅Π΄Π½Π°Ρ‚Π° ΡΡ‚ΡŠΠΏΠΊΠ° Π΅ ΠΈΠ·Π²Π΅ΠΆΠ΄Π°Π½Π΅ Π½Π° Π΄Π°Π½Π½ΠΈ към Ρ‚Π΅Ρ€ΠΌΠΈΠ½Π°Π»Π°:

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()

Бамият скрипт Π΅ Π΄ΠΎΡΡ‚ΡŠΠΏΠ΅Π½ Π½Π° GitHub. НСка сС ΠΎΠΏΠΈΡ‚Π°ΠΌΠ΅ Π΄Π° Π³ΠΎ стартирамС Π½Π° тСстова ΠΏΠ»Π°Ρ‚Ρ„ΠΎΡ€ΠΌΠ°, ΠΊΡŠΠ΄Π΅Ρ‚ΠΎ fio Ρ€Π°Π±ΠΎΡ‚ΠΈ, пишС Π² bcache ΠΈ ΠΈΠ·Π²ΠΈΠΊΠ²Π° udevadm monitor:

ΠžΡ‚ High Ceph Latency Π΄ΠΎ Kernel Patch с ΠΏΠΎΠΌΠΎΡ‰Ρ‚Π° Π½Π° eBPF/BCC
Най-накрая! Π‘Π΅Π³Π° Π²ΠΈΠΆΠ΄Π°ΠΌΠ΅, Ρ‡Π΅ Ρ‚ΠΎΠ²Π°, ΠΊΠΎΠ΅Ρ‚ΠΎ ΠΈΠ·Π³Π»Π΅ΠΆΠ΄Π° ΠΊΠ°Ρ‚ΠΎ Π±Π»ΠΎΠΊΠΈΡ€Π°Ρ‰ΠΎ bcache устройство, Π²ΡΡŠΡ‰Π½ΠΎΡΡ‚ Π΅ забавящо ΠΏΠΎΠ²ΠΈΠΊΠ²Π°Π½Π΅ generic_make_request() Π·Π° ΠΊΠ΅ΡˆΠΈΡ€Π°Π½ диск.

Π Π°Π·Ρ€ΠΎΠ²Π΅Ρ‚Π΅ Π² ядрото

Какво Ρ‚ΠΎΡ‡Π½ΠΎ забавя ΠΏΠΎ Π²Ρ€Π΅ΠΌΠ΅ Π½Π° ΠΏΡ€Π΅Π΄Π°Π²Π°Π½Π΅ Π½Π° заявка? Π’ΠΈΠΆΠ΄Π°ΠΌΠ΅, Ρ‡Π΅ забавянСто възниква Π΄ΠΎΡ€ΠΈ ΠΏΡ€Π΅Π΄ΠΈ Π½Π°Ρ‡Π°Π»ΠΎΡ‚ΠΎ Π½Π° ΠΎΡ‚Ρ‡ΠΈΡ‚Π°Π½Π΅Ρ‚ΠΎ Π½Π° заявката, Ρ‚.Π΅. ΠΎΡ‚Ρ‡ΠΈΡ‚Π°Π½Π΅Ρ‚ΠΎ Π½Π° ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½Π° заявка Π·Π° ΠΏΠΎ-Π½Π°Ρ‚Π°Ρ‚ΡŠΡˆΠ½ΠΎ ΠΈΠ·Π²Π΅ΠΆΠ΄Π°Π½Π΅ Π½Π° статистика Π·Π° нСя (/proc/diskstats ΠΈΠ»ΠΈ iostat) всС ΠΎΡ‰Π΅ Π½Π΅ Π΅ Π·Π°ΠΏΠΎΡ‡Π½Π°Π»ΠΎ. Π’ΠΎΠ²Π° ΠΌΠΎΠΆΠ΅ лСсно Π΄Π° сС ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΈ Ρ‡Ρ€Π΅Π· стартиранС Π½Π° iostat ΠΏΡ€ΠΈ Π²ΡŠΠ·ΠΏΡ€ΠΎΠΈΠ·Π²Π΅ΠΆΠ΄Π°Π½Π΅ Π½Π° ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠ°, ΠΈΠ»ΠΈ БиолатСнтност Π½Π° BCC скрипт, ΠΊΠΎΠ΅Ρ‚ΠΎ сС основава Π½Π° Π½Π°Ρ‡Π°Π»ΠΎΡ‚ΠΎ ΠΈ края Π½Π° ΠΎΡ‚Ρ‡ΠΈΡ‚Π°Π½Π΅Ρ‚ΠΎ Π½Π° заявката. Нито Π΅Π΄Π½Π° ΠΎΡ‚ Ρ‚Π΅Π·ΠΈ ΠΏΠΎΠΌΠΎΡ‰Π½ΠΈ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΈ няма Π΄Π° ΠΏΠΎΠΊΠ°ΠΆΠ΅ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠΈ Π·Π° заявки към ΠΊΠ΅ΡˆΠΈΡ€Π°Π½ΠΈΡ диск.

Ако Ρ€Π°Π·Π³Π»Π΅Π΄Π°ΠΌΠ΅ функцията generic_make_request(), Ρ‚ΠΎΠ³Π°Π²Π° Ρ‰Π΅ Π²ΠΈΠ΄ΠΈΠΌ, Ρ‡Π΅ ΠΏΡ€Π΅Π΄ΠΈ заявката Π΄Π° Π·Π°ΠΏΠΎΡ‡Π½Π΅ ΠΎΡ‚Ρ‡ΠΈΡ‚Π°Π½Π΅, сС ΠΈΠ·Π²ΠΈΠΊΠ²Π°Ρ‚ ΠΎΡ‰Π΅ Π΄Π²Π΅ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ. ΠΏΡŠΡ€Π²ΠΎ - generic_make_request_checks(), ΠΈΠ·Π²ΡŠΡ€ΡˆΠ²Π° ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΈ Π½Π° лСгитимността Π½Π° заявката относно настройкитС Π½Π° диска. Π²Ρ‚ΠΎΡ€ΠΎ - blk_queue_enter(), ΠΊΠΎΠΉΡ‚ΠΎ ΠΈΠΌΠ° интСрСсно прСдизвикатСлство wait_event_interruptible():

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));

Π’ Π½Π΅Π³ΠΎ ядрото Ρ‡Π°ΠΊΠ° ΠΎΠΏΠ°ΡˆΠΊΠ°Ρ‚Π° Π΄Π° сС Ρ€Π°Π·ΠΌΡ€Π°Π·ΠΈ. Π”Π° ΠΈΠ·ΠΌΠ΅Ρ€ΠΈΠΌ Π·Π°ΠΊΡŠΡΠ½Π΅Π½ΠΈΠ΅Ρ‚ΠΎ 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    	|                                    	|

ИзглСТда, Ρ‡Π΅ смС Π±Π»ΠΈΠ·ΠΎ Π΄ΠΎ Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅. Π€ΡƒΠ½ΠΊΡ†ΠΈΠΈΡ‚Π΅, ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Π½ΠΈ Π·Π° замразяванС/отмразяванС Π½Π° опашка са blk_mq_freeze_queue ΠΈ blk_mq_unfreeze_queue. Π’Π΅ сС ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Ρ‚, ΠΊΠΎΠ³Π°Ρ‚ΠΎ Π΅ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ Π΄Π° сС промСнят настройкитС Π½Π° ΠΎΠΏΠ°ΡˆΠΊΠ°Ρ‚Π° Π½Π° заявкитС, ΠΊΠΎΠΈΡ‚ΠΎ са ΠΏΠΎΡ‚Π΅Π½Ρ†ΠΈΠ°Π»Π½ΠΎ опасни Π·Π° заявкитС Π² Ρ‚Π°Π·ΠΈ опашка. ΠŸΡ€ΠΈ ΠΎΠ±Π°ΠΆΠ΄Π°Π½Π΅ blk_mq_freeze_queue() функция blk_freeze_queue_start() Π±Ρ€ΠΎΡΡ‡ΡŠΡ‚ сС ΡƒΠ²Π΅Π»ΠΈΡ‡Π°Π²Π° q->mq_freeze_depth. Π‘Π»Π΅Π΄ Ρ‚ΠΎΠ²Π° ядрото ΠΈΠ·Ρ‡Π°ΠΊΠ²Π° ΠΎΠΏΠ°ΡˆΠΊΠ°Ρ‚Π° Π΄Π° сС ΠΈΠ·ΠΏΡ€Π°Π·Π½ΠΈ blk_mq_freeze_queue_wait().

Π’Ρ€Π΅ΠΌΠ΅Ρ‚ΠΎ, Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ Π·Π° изчистванС Π½Π° Ρ‚Π°Π·ΠΈ опашка, Π΅ Π΅ΠΊΠ²ΠΈΠ²Π°Π»Π΅Π½Ρ‚Π½ΠΎ Π½Π° латСнтността Π½Π° диска, Ρ‚ΡŠΠΉ ΠΊΠ°Ρ‚ΠΎ ядрото Ρ‡Π°ΠΊΠ° Π·Π°Π²ΡŠΡ€ΡˆΠ²Π°Π½Π΅Ρ‚ΠΎ Π½Π° всички ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ Π² ΠΎΠΏΠ°ΡˆΠΊΠ°Ρ‚Π°. Π‘Π»Π΅Π΄ ΠΊΠ°Ρ‚ΠΎ ΠΎΠΏΠ°ΡˆΠΊΠ°Ρ‚Π° Π΅ ΠΏΡ€Π°Π·Π½Π°, ΠΏΡ€ΠΎΠΌΠ΅Π½ΠΈΡ‚Π΅ Π² настройкитС сС ΠΏΡ€ΠΈΠ»Π°Π³Π°Ρ‚. Π‘Π»Π΅Π΄ ΠΊΠΎΠ΅Ρ‚ΠΎ сС Π½Π°Ρ€ΠΈΡ‡Π° blk_mq_unfreeze_queue(), намалявайки брояча freeze_depth.

Π‘Π΅Π³Π° Π·Π½Π°Π΅ΠΌ Π΄ΠΎΡΡ‚Π°Ρ‚ΡŠΡ‡Π½ΠΎ, Π·Π° Π΄Π° ΠΊΠΎΡ€ΠΈΠ³ΠΈΡ€Π°ΠΌΠ΅ ситуацията. ΠšΠΎΠΌΠ°Π½Π΄Π°Ρ‚Π° Π·Π° задСйстванС udevadm ΠΏΡ€Π΅Π΄ΠΈΠ·Π²ΠΈΠΊΠ²Π° ΠΏΡ€ΠΈΠ»Π°Π³Π°Π½Π΅Ρ‚ΠΎ Π½Π° настройкитС Π·Π° Π±Π»ΠΎΠΊΠΎΠ²ΠΎΡ‚ΠΎ устройство. Π’Π΅Π·ΠΈ настройки са описани Π² ΠΏΡ€Π°Π²ΠΈΠ»Π°Ρ‚Π° Π½Π° udev. МоТСм Π΄Π° ΠΎΡ‚ΠΊΡ€ΠΈΠ΅ΠΌ ΠΊΠΎΠΈ настройки замразяват ΠΎΠΏΠ°ΡˆΠΊΠ°Ρ‚Π°, ΠΊΠ°Ρ‚ΠΎ сС ΠΎΠΏΠΈΡ‚Π°ΠΌΠ΅ Π΄Π° Π³ΠΈ ΠΏΡ€ΠΎΠΌΠ΅Π½ΠΈΠΌ Ρ‡Ρ€Π΅Π· sysfs ΠΈΠ»ΠΈ ΠΊΠ°Ρ‚ΠΎ ΠΏΠΎΠ³Π»Π΅Π΄Π½Π΅ΠΌ изходния ΠΊΠΎΠ΄ Π½Π° ядрото. МоТСм ΡΡŠΡ‰ΠΎ Π΄Π° ΠΎΠΏΠΈΡ‚Π°ΠΌΠ΅ ΠΏΠΎΠΌΠΎΡ‰Π½Π°Ρ‚Π° ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠ° BCC прослСди, ΠΊΠΎΠΉΡ‚ΠΎ Ρ‰Π΅ ΠΈΠ·Π²Π΅Π΄Π΅ слСди Π½Π° стСка Π½Π° ядрото ΠΈ потрСбитСлското пространство Π·Π° всяко ΠΏΠΎΠ²ΠΈΠΊΠ²Π°Π½Π΅ към Ρ‚Π΅Ρ€ΠΌΠΈΠ½Π°Π»Π° blk_freeze_queue, Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€:

~# /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]

ΠŸΡ€Π°Π²ΠΈΠ»Π°Ρ‚Π° Π½Π° Udev сС промСнят доста рядко ΠΈ ΠΎΠ±ΠΈΠΊΠ½ΠΎΠ²Π΅Π½ΠΎ Ρ‚ΠΎΠ²Π° сС случва ΠΏΠΎ ΠΊΠΎΠ½Ρ‚Ρ€ΠΎΠ»ΠΈΡ€Π°Π½ Π½Π°Ρ‡ΠΈΠ½. Π’Π°ΠΊΠ° Ρ‡Π΅ Π²ΠΈΠΆΠ΄Π°ΠΌΠ΅, Ρ‡Π΅ Π΄ΠΎΡ€ΠΈ ΠΏΡ€ΠΈΠ»Π°Π³Π°Π½Π΅Ρ‚ΠΎ Π½Π° Π²Π΅Ρ‡Π΅ Π·Π°Π΄Π°Π΄Π΅Π½ΠΈΡ‚Π΅ стойности причинява скок Π² забавянСто Π½Π° ΠΏΡ€Π΅Ρ…Π²ΡŠΡ€Π»ΡΠ½Π΅Ρ‚ΠΎ Π½Π° заявката ΠΎΡ‚ ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅Ρ‚ΠΎ към диска. Π Π°Π·Π±ΠΈΡ€Π° сС, Π³Π΅Π½Π΅Ρ€ΠΈΡ€Π°Π½Π΅Ρ‚ΠΎ Π½Π° udev ΡΡŠΠ±ΠΈΡ‚ΠΈΡ, ΠΊΠΎΠ³Π°Ρ‚ΠΎ няма ΠΏΡ€ΠΎΠΌΠ΅Π½ΠΈ Π² конфигурацията Π½Π° диска (Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€ устройството Π½Π΅ Π΅ ΠΌΠΎΠ½Ρ‚ΠΈΡ€Π°Π½ΠΎ/ΠΏΡ€Π΅ΠΊΡŠΡΠ½Π°Ρ‚ΠΎ) Π½Π΅ Π΅ Π΄ΠΎΠ±Ρ€Π° ΠΏΡ€Π°ΠΊΡ‚ΠΈΠΊΠ°. Π’ΡŠΠΏΡ€Π΅ΠΊΠΈ Ρ‚ΠΎΠ²Π° ΠΌΠΎΠΆΠ΅ΠΌ Π΄Π° ΠΏΠΎΠΌΠΎΠ³Π½Π΅ΠΌ Π½Π° ядрото Π΄Π° Π½Π΅ Π²ΡŠΡ€ΡˆΠΈ Π½Π΅Π½ΡƒΠΆΠ½Π° Ρ€Π°Π±ΠΎΡ‚Π° ΠΈ Π΄Π° Π·Π°ΠΌΡ€Π°Π·ΠΈΠΌ ΠΎΠΏΠ°ΡˆΠΊΠ°Ρ‚Π° Π·Π° заявки, Π°ΠΊΠΎ Π½Π΅ Π΅ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ. Ρ‚Ρ€ΠΈ малък Π°Π½Π³Π°ΠΆΠΈΡ€Π°ΠΌ ΠΊΠΎΡ€ΠΈΠ³ΠΈΡ€Π°ΠΉΡ‚Π΅ ситуацията.

Π—Π°ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅

eBPF Π΅ ΠΌΠ½ΠΎΠ³ΠΎ гъвкав ΠΈ ΠΌΠΎΡ‰Π΅Π½ инструмСнт. Π’ статията Ρ€Π°Π·Π³Π»Π΅Π΄Π°Ρ…ΠΌΠ΅ Π΅Π΄ΠΈΠ½ практичСски случай ΠΈ дСмонстрирахмС ΠΌΠ°Π»ΠΊΠ° част ΠΎΡ‚ Ρ‚ΠΎΠ²Π°, ΠΊΠΎΠ΅Ρ‚ΠΎ ΠΌΠΎΠΆΠ΅ Π΄Π° сС Π½Π°ΠΏΡ€Π°Π²ΠΈ. Ако сС интСрСсуватС ΠΎΡ‚ Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Π²Π°Π½Π΅Ρ‚ΠΎ Π½Π° BCC ΠΏΠΎΠΌΠΎΡ‰Π½ΠΈ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΈ, струва си Π΄Π° Ρ€Π°Π·Π³Π»Π΅Π΄Π°Ρ‚Π΅ ΠΎΡ„ΠΈΡ†ΠΈΠ°Π»Π΅Π½ ΡƒΡ€ΠΎΠΊ, ΠΊΠΎΠ΅Ρ‚ΠΎ Π΄ΠΎΠ±Ρ€Π΅ описва основитС.

Има ΠΈ Π΄Ρ€ΡƒΠ³ΠΈ интСрСсни инструмСнти Π·Π° отстраняванС Π½Π° Π³Ρ€Π΅ΡˆΠΊΠΈ ΠΈ ΠΏΡ€ΠΎΡ„ΠΈΠ»ΠΈΡ€Π°Π½Π΅, Π±Π°Π·ΠΈΡ€Π°Π½ΠΈ Π½Π° eBPF. Π•Π΄ΠΈΠ½ ΠΎΡ‚ тях - bpftrace, ΠΊΠΎΠ΅Ρ‚ΠΎ Π²ΠΈ позволява Π΄Π° ΠΏΠΈΡˆΠ΅Ρ‚Π΅ ΠΌΠΎΡ‰Π½ΠΈ Π΅Π΄Π½ΠΎΡ€Π΅Π΄ΠΎΠ²ΠΈ ΠΈ ΠΌΠ°Π»ΠΊΠΈ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΈ Π½Π° ΠΏΠΎΠ΄ΠΎΠ±Π΅Π½ Π½Π° awk Π΅Π·ΠΈΠΊ. Π”Ρ€ΡƒΠ³ - ebpf_exporter, Π²ΠΈ позволява Π΄Π° ΡΡŠΠ±ΠΈΡ€Π°Ρ‚Π΅ ΠΏΠΎΠΊΠ°Π·Π°Ρ‚Π΅Π»ΠΈ Π½Π° ниско Π½ΠΈΠ²ΠΎ с висока Ρ€Π°Π·Π΄Π΅Π»ΠΈΡ‚Π΅Π»Π½Π° способност Π΄ΠΈΡ€Π΅ΠΊΡ‚Π½ΠΎ във вашия ΡΡŠΡ€Π²ΡŠΡ€ prometheus, с Π²ΡŠΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ ΠΏΠΎ-късно Π΄Π° ΠΏΠΎΠ»ΡƒΡ‡Π°Π²Π°Ρ‚Π΅ красиви Π²ΠΈΠ·ΡƒΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ ΠΈ Π΄ΠΎΡ€ΠΈ прСдупрСТдСния.

Π˜Π·Ρ‚ΠΎΡ‡Π½ΠΈΠΊ: www.habr.com

ДобавянС Π½Π° Π½ΠΎΠ² ΠΊΠΎΠΌΠ΅Π½Ρ‚Π°Ρ€