معمای تاخیر: چرا بافرهای سخت‌افزاری کارت شبکه کارایی AQM را مختل می‌کنند

در سرورها و گیت‌وی‌های پرظرفیت VPN که ترافیک رمزنگاری‌شده عظیمی را هدایت می‌کنند، مدیران شبکه اغلب با یک ناهماهنگی عجیب مواجه می‌شوند: با وجود فعال‌سازی پیشرفته‌ترین الگوریتم‌های مدیریت صف فعال (Active Queue Management - AQM) نظیر FQ-CoDel یا CAKE روی اینترفیس شبکه، در زمان اوج مصرف شاهد جهش‌های تاخیر بین ۵۰ تا ۱۵۰ میلی‌ثانیه هستند. ترافیک تماس‌های صوتی (VoIP) دچار قطعی‌های ریز می‌شود، لرزش تاخیر (Jitter) در بازی‌ها افزایش می‌یابد و نرخ انتقال پروتکل TCP دستخوش نوسان می‌گردد.

ریشه این چالش در سیستم مدیریت صف نرم‌افزاری هسته (qdisc) نیست، بلکه ناشی از انباشت داده در لایه‌ای پایین‌تر یعنی حلقه انتقال درایور (TX ring) در کارت شبکه فیزیکی (NIC) است.

کارت‌های شبکه اترنت امروزی از حلقه‌های توصیف‌گر بزرگی (معمولاً ۱۰۲۴، ۲۰۴۸ یا ۴۰۹6 خانه) استفاده می‌کنند تا مطمئن شوند در سرعت‌های گیگابیتی هیچ‌گاه با کمبود داده مواجه نمی‌شوند. برای جریان‌های معمولی TCP، هسته لینوکس از سازوکار TCP Small Queues (TSQ) بهره می‌برد تا حجم حافظه اشغال‌شده در لایه سوکت را مهار کند. با این حال، گیت‌وی‌های VPN ترافیک بسته‌بندی‌شده و کپسوله را جابجا می‌کنند:

[ بسته‌های برنامه‌ها TCP/UDP ] ---> [ موتور تونل VPN (WireGuard / IPsec) ]
                                                    |
                                                    v
                                  [ دیتاگرام‌های خارجی کپسوله‌شده UDP ]
                                                    |
                                                    v
                                         [ پشته خروجی هسته لینوکس ]

برای پشته شبکه لینوکس، خروجی این تونل‌ها شبیه بسته‌های ساده UDP بدون بازخورد کنترل ازدحام لایه سوکت به نظر می‌رسد. وقتی موتور VPN حجم زیادی از بسته‌ها را روانه کارت شبکه می‌کند، بافر سخت‌افزاری در چند میکروثانیه کاملاً پر می‌شود.

تا زمانی که بسته‌ها در بافر کارت شبکه محبوس هستند، الگوریتم‌های هوشمند qdisc نمی‌توانند اولویت بسته‌ها را بازآرایی کنند یا آنها را به موقع تخلیه نمایند. در نتیجه بسته‌های تعاملی و حساس به زمان (مانند بسته‌های صوتی، هندشیک‌های رمزنگاری و DNS) پشت مگابایت‌ها بار دانلود فایل به انتظار می‌مانند.

ساختار درونی dql: الگوریتم خودکار تنظیم بایت‌ها

برای از بین بردن پدیده بافربلوت در لایه درایور، هسته لینوکس زیرسیستم Byte Queue Limits (BQL) را با استفاده از کتابخانه Dynamic Queue Limits (DQL) در فایل lib/dynamic_queue_limits.c معرفی کرد.

این نوآوری توسط تام هربرت (Tom Herbert) ارائه شد تا نقطه انباشت صف را از حافظه غیرقابل دسترس درایور به صف نرم‌افزاری تحت نظارت هسته منتقل کند. برخلاف روش‌های قدیمی که تعداد بسته‌ها را معیار قرار می‌دادند، BQL مستقیماً با تعداد بایت‌های واقعی کار می‌کند؛ چرا که یک بسته کوتاه ۶۴ بایتی همان مقدار خانه توصیف‌گر سخت‌افزاری را اشغال می‌کند که یک فریم تجمیع‌شده ۶۴ کیلوبایتی (GSO) می‌گیرد، ولی زمان تخلیه فیزیکی آن روی کابل هزار برابر کمتر است.

+-------------------------------------------------------------+
|               مدیریت صف نرم‌افزاری هسته (qdisc)              |
|                  FQ-CoDel / CAKE / PFIFO_FAST               |
+-------------------------------------------------------------+
                               |
                   netdev_tx_sent_queue(dev_queue, bytes)
                               v
+-------------------------------------------------------------+
|               حلقه انتقال سخت‌افزاری درایور (TX Ring)       |
|    حداکثر ظرفیت: ۱۰۲۴ توصیف‌گر                              |
|    محدوده پویای BQL: بین ۳,۰۰۰ تا ۱۲,۰۰۰ بایت               |
+-------------------------------------------------------------+
                               |
                   (انتقال فیزیکی داده روی کابل)
                               |
                 netdev_tx_completed_queue(dev_queue, pkts, bytes)
                               v
+-------------------------------------------------------------+
|                    موتور محاسباتی dql.c                     |
|           محاسبه زمان تخلیه و به‌روزرسانی سقف بایت           |
+-------------------------------------------------------------+

این سازوکار وضعیت پویای خود را با تکیه بر سه شاخص کلیدی مدیریت می‌کند:

  1. num_queued: کل حجم بایت‌هایی که به کارت شبکه تحویل داده شده و هنوز تایید اتمام ارسال آنها دریافت نشده است.
  2. adj_limit: حد مجاز فعلی برای نگهداری بایت در درایور پیش از توقف ارسال از صف نرم‌افزاری.
  3. lowest_slack: کمترین مقدار مازاد بایت مشاهده‌شده در طول یک بازه ارزیابی.

چرخه تصمیم‌گیری در dynamic_queue_limits.c

هنگام تحویل هر بسته به درایور، تابع netdev_tx_sent_queue() فراخوانی می‌شود. اگر مقدار num_queued از حد مشخص تجاوز کند، BQL موقتاً دریافت بسته از هسته را متوقف می‌کند (netif_tx_stop_queue()). بنابراین بسته‌های اضافی به جای ورود به کارت، در صف نرم‌افزاری qdisc جمع می‌شوند و الگوریتم FQ-CoDel آنها را اولویت‌بندی می‌نماید.

زمانی که کارت شبکه ارسال فیزیکی را به پایان می‌رساند و وقفه اعلام می‌کند، تابع netdev_tx_completed_queue() اجرا می‌شود:

  • حالت اضافه بافر (slack > 0): اگر کارت شبکه در طول چرخه همواره بایت اضافه داشته و هیچ‌گاه خالی نمانده باشد، نشان می‌دهد سقف قبلی بیش از حد نیاز بوده است. بنابراین BQL مقدار adj_limit را کاهش می‌دهد تا تاخیر به حداقل برسد.
  • حالت خطر خالی ماندن (starvation): اگر بافر کارت قبل از دریافت محموله بعدی کاملاً تخلیه شده باشد، BQL به آرامی adj_limit را افزایش می‌دهد تا تمام ظرفیت پهنای باند خط حفظ شود.

این حلقه کنترلی مداوم باعث می‌شود حجم داده در درایور در کمترین مقدار لازم برای استفاده صددرصدی از کابل فیزیکی باقی بماند (غالباً بین ۴ تا ۱۲ کیلوبایت در پورت‌های ۱۰ گیگابیتی، که معادل کمتر از ۵۰ میکروثانیه تاخیر است).

مزایای حیاتی BQL در گیت‌وی‌های تونل VPN

در سرورهای مبتنی بر WireGuard، RiceGuard و IPsec، فعال بودن BQL مزیت‌های چشمگیری ایجاد می‌کند:

۱. مهار فوران‌های فریم‌های تجمیعی (GSO/GRO)

تونل‌های پیشرفته از روش Generic Segmentation Offload بهره می‌برند. در زمان جابجایی ترافیک حجیم، هسته بسته‌ها را به قطعات ۶۴ کیلوبایتی تبدیل می‌کند. در نبود BQL، چند فریم محدود می‌توانند بافر سخت‌افزاری را برای ده‌ها میلی‌ثانیه مسدود کنند. با BQL، کارت فقط حجم مجازی را دریافت می‌کند و بقیه بسته‌ها در صف هسته به شکل عادلانه توزیع می‌شوند.

۲. بهره‌وری کامل از زمان‌بندی FQ-CoDel

وقتی بسته‌ها در صف نرم‌افزاری نگه داشته شوند، FQ-CoDel هر جریان را بر اساس هش مبدا و مقصد تفکیک می‌کند. بسته‌های صوتی سبک، پینگ‌ها و تاییدیه‌های رمزنگاری بدون گیر افتادن پشت فایل‌های سنگین، فوراً در کانال‌های مستقل عبور داده می‌شوند.

۳. حفظ ساختار اولویت‌بندی RiceGuard

در گیت‌وی‌هایی که از RiceGuard با لایه‌بندی چندتونلی و صف‌های پویا استفاده می‌کنند، BQL تضمین می‌کند تصمیمات مدیریتی بلافاصله روی رسانه فیزیکی اعمال شده و در بافرهای سخت‌افزاری درایور خنثی نشوند.

بررسی و سنجش وضعیت BQL در لینوکس

وضعیت BQL روی سرورهای عملیاتی به راحتی از طریق شاخه مجازی sysfs قابل مشاهده است. به ازای هر اینترفیس و هر صف ارسال، اطلاعات در مسیر /sys/class/net/<interface>/queues/tx-<N>/byte_queue_limits/ قرار دارد:

# بررسی متغیرهای BQL در اولین صف اینترفیس eth0
ls -la /sys/class/net/eth0/queues/tx-0/byte_queue_limits/

# پارامترهای اصلی:
# - limit: سقف مجاز محاسبه‌شده بر حسب بایت
# - limit_max: بالاترین حد مجاز تنظیمی
# - limit_min: پایین‌ترین آستانه برای جلوگیری از وقفه
# - hold_time: بازه زمانی ثبات ارزیابی (میلی‌ثانیه)
# - bytes: مجموع بایت‌های ارسال‌شده
# - packets: مجموع بسته‌های ارسال‌شده

برای رصد لحظه‌ای تغییر سقف بایت‌ها هنگام انتقال بار سنگین:

watch -n 1 'for q in /sys/class/net/eth0/queues/tx-*/byte_queue_limits/limit; do
  echo -n "$q: "; cat "$q";
done'

اگر شاخه byte_queue_limits در مسیر صف موجود نباشد، به این معناست که درایور کارت فاقد پشتیبانی از توابع BQL است. درایورهای پرکاربرد در سرورهای سازمانی مانند ixgbe، i40e، ice، bnxt_en و mlx5 به طور استاندارد مجهز به BQL هستند.

توصیه‌های کاربردی برای تنظیم گیت‌وی VPN

برای دستیابی به حداقل تاخیر در سرورهای VPN، رعایت این موارد پیشنهاد می‌شود:

۱. پیاده‌سازی همزمان FQ-CoDel یا CAKE

سازوکار BQL بسته‌ها را مرتب نمی‌کند، بلکه بستر عملکرد دقیق الگوریتم‌های صف‌بندی را فراهم می‌سازد. پیکربندی fq_codel را روی کارت فیزیکی اعمال نمایید:

# اعمال fq_codel روی کارت شبکه فیزیکی اصلی
sudo tc qdisc replace dev eth0 root fq_codel ecn

۲. حفظ اندازه پیش‌فرض حلقه‌های سخت‌افزاری

تغییر دستی و کوچک کردن بیش از حد بافر با دستوراتی نظیر ethtool -G eth0 tx 128 کار درستی نیست؛ چرا که BQL خود ترافیک را کنترل می‌کند و دست‌نخورده ماندن حلقه کارت مانع از بروز خطای کمبود پردازنده در بار ترافیکی شدید می‌شود.

۳. نظارت بر کارت فیزیکی خروجی

کارت‌های مجازی تونل مانند wg0 یا tun0 حلقه فیزیکی ندارند و BQL روی آنها تعریف نمی‌شود. پایش و اعمال صف باید روی پورت فیزیکی (eth0) صورت گیرد که دیتای رمزنگاری‌شده را به سمت ارائه‌دهنده اینترنت هدایت می‌کند.