NVMe Queue Pair Memisahkan Submission Command dari Completion

SSD NVMe tidak mengharuskan CPU menyerahkan setiap storage command langsung ke register perangkat lalu menunggu command tersebut selesai. Sebaliknya, NVMe menempatkan record command dan completion dalam queue yang berada di memori host. Controller membaca command yang menunggu dari submission queue dan menulis hasil ke completion queue terkait.

Susunan ini cocok untuk storage PCIe berkecepatan tinggi. Controller SSD modern dapat memproses banyak operasi sekaligus melalui flash channel, die internal, dan pipeline controller. Model queue memungkinkan software menjaga hardware paralel tersebut tetap sibuk tanpa rangkaian panjang penyerahan command secara sinkron.

Mekanisme utamanya adalah queue pair: satu submission queue membawa command menuju controller, sedangkan satu completion queue membawa completion entry kembali ke host. Command dan hasil bergerak ke arah berlawanan, dan setiap sisi memajukan posisi queue-nya sendiri.

Submission queue menyimpan pekerjaan yang dapat diambil controller

Submission queue adalah circular buffer di memori host. Software menyiapkan NVMe command pada slot yang tersedia, memajukan tail queue, lalu menulis nilai tail baru ke submission queue doorbell register milik controller.

Doorbell tidak membawa seluruh command. Register tersebut memberi tahu controller bahwa ada entry tambahan yang siap di memori. Controller kemudian dapat mengambil entry tersebut melalui PCIe menggunakan DMA.

Perbedaan ini penting bagi overhead. Sebuah command berisi field seperti opcode, namespace identifier, command identifier, dan referensi data buffer. Menyimpan struktur tersebut di memori menghindari kebutuhan mengodekan request lengkap melalui penulisan berulang ke register perangkat.

Controller mempertahankan pandangannya sendiri atas head submission queue ketika mengonsumsi entry. Software host tidak boleh menimpa slot yang masih digunakan. Struktur circular karena itu bergantung pada koordinasi posisi head dan tail, bukan sekadar daftar append-only.

Queue depth menetapkan jumlah maksimum entry yang dapat menempati ring, tetapi queue depth tidak identik dengan concurrency perangkat yang berguna. Controller dapat menerima queue yang dalam sementara media flash, firmware, atau karakteristik workload membatasi jumlah pekerjaan yang dapat dieksekusi secara efisien pada satu waktu.

Completion queue mengembalikan status tanpa menggantikan jalur command

Ketika sebuah command mencapai titik completion, controller menulis completion queue entry ke memori host. Entry tersebut mengidentifikasi command yang selesai dan melaporkan informasi status. Controller kemudian memajukan posisi completion-nya.

Software host mengonsumsi completion entry dari sisi lain ring dan pada akhirnya memperbarui completion queue head doorbell. Pembaruan ini memberi tahu controller slot completion mana yang sudah direklamasi dan dapat digunakan kembali.

Pemisahan submission dari completion mencegah aliran command menjadi pertukaran lockstep tunggal. Software dapat mengirim command B, C, dan D sebelum command A selesai, selama kapasitas queue dan aturan ordering tingkat lebih tinggi mengizinkannya.

Ini merupakan salah satu sumber utama skalabilitas NVMe. Storage request dapat tetap outstanding sementara CPU menjalankan kode lain, controller menjadwalkan operasi flash, dan transfer PCIe memindahkan data.

Completion entry juga jauh lebih kecil daripada data yang ditransfer oleh operasi read atau write biasa. Record queue mengoordinasikan operasi; payload umumnya bergerak secara terpisah melalui DMA menuju atau dari buffer host yang dijelaskan oleh command.

Command identifier menghubungkan hasil dengan request yang outstanding

Command dapat selesai dalam pola waktu yang berbeda dari urutan software mengirimkannya. Karena itu, NVMe tidak dapat mengandalkan aturan sederhana bahwa completion berikutnya selalu milik request outstanding yang paling lama.

Setiap command membawa command identifier yang dipilih software host untuk submission queue tersebut. Completion entry mengembalikan identifier itu sehingga driver dapat mengaitkan hasil dengan state request-nya.

Identifier hanya perlu unik di antara command yang masih outstanding pada submission queue terkait. Setelah command selesai dan software mengonsumsi hasilnya, identifier dapat digunakan kembali sesuai bookkeeping driver.

Dengan mekanisme ini, software dapat mempertahankan banyak request independen tanpa menempelkan pointer software besar langsung pada setiap hardware completion record. Driver dapat memakai identifier sebagai indeks atau lookup key ke tabel request miliknya.

Asosiasi queue juga penting. Nilai command identifier yang identik dapat muncul pada submission queue berbeda tanpa merujuk pada operasi yang sama.

Phase tag membedakan completion baru setelah ring wrap

Circular queue pada akhirnya berputar dari slot terakhir kembali ke slot pertama. Completion ring karena itu membutuhkan cara yang andal bagi software host untuk membedakan entry yang baru ditulis dari byte lama yang tersisa dari putaran sebelumnya.

NVMe completion entry menyertakan phase tag untuk tujuan ini. Controller menulis entry dengan nilai phase yang diharapkan untuk putaran ring saat ini. Ketika controller melakukan wrap, phase yang diharapkan berubah.

Software host memeriksa phase tag pada slot completion berikutnya. Nilai yang cocok menunjukkan bahwa controller telah menghasilkan completion baru di sana. Nilai yang tidak cocok berarti software telah mencapai entry yang belum baru untuk putaran saat ini.

Mekanisme ini menghindari kebutuhan membersihkan seluruh completion ring setelah setiap penggunaan. Isi entry lama dapat tetap berada di memori karena transisi phase menyediakan sinyal freshness.

Mekanismenya kecil, tetapi penting bagi ring yang berada di memori dan diperbarui kedua sisi secara asynchronous.

Doorbell membuat progres queue terlihat oleh sisi lain

Queue berada di memori host, tetapi memory write saja tidak menggantikan seluruh komunikasi perangkat. NVMe memakai memory-mapped doorbell register untuk mengomunikasikan progres queue.

Untuk submission queue, host memperbarui tail doorbell setelah menempatkan command baru di ring. Untuk completion queue, host memperbarui head doorbell setelah mengonsumsi hasil.

Write tersebut mendefinisikan progres ownership. Controller perlu mengetahui submission entry mana yang tersedia, dan perlu mengetahui completion entry mana yang sudah dilepas host.

Ordering yang benar penting. Software harus membuat command entry terlihat sebelum memberi sinyal tail yang mengekspos entry tersebut kepada controller. Constraint ordering serupa berlaku saat mereklamasi ruang queue. Kode driver dan primitive DMA sistem operasi menangani detail memory ordering spesifik arsitektur yang diperlukan di sekitar interaksi ini.

Doorbell write tetap memiliki biaya karena menargetkan ruang PCIe yang terlihat perangkat, bukan memori cacheable biasa. Driver dapat mengurangi tekanan dengan melakukan batching beberapa command sebelum satu pembaruan tail ketika kebutuhan latency dan kebijakan implementasi mengizinkannya.

Banyak queue pair mengurangi sinkronisasi bersama

NVMe mendukung banyak I/O submission queue dan completion queue. Sistem operasi dapat memetakan queue pair ke CPU atau kelompok CPU sehingga core yang tidak berkaitan tidak terus-menerus berebut satu storage queue global.

Desain per-CPU atau yang mempertimbangkan locality dapat menjaga queue lock, metadata request, interrupt, dan pemrosesan completion lebih dekat ke CPU yang mengirim pekerjaan. Desain ini juga dapat mengurangi perpindahan cache line akibat beberapa core memperbarui state producer dan consumer yang sama.

Pemetaan tepatnya merupakan keputusan sistem operasi dan driver. Sebuah mesin tidak otomatis mendapat performa lebih tinggi hanya dengan membuat jumlah queue maksimum. Queue tambahan memakai memori dan resource controller, sementara pemetaan yang buruk dapat menambah kompleksitas tanpa menghilangkan bottleneck nyata.

Queue count dan queue depth menyelesaikan masalah berbeda. Lebih banyak queue dapat mengurangi contention software dan mengekspos stream independen. Depth yang lebih besar memungkinkan lebih banyak command outstanding dalam satu queue. Keduanya dapat membantu throughput, tetapi tidak ada yang menjamin latency lebih rendah.

Interrupt dan polling memberi trade-off completion yang berbeda

Controller dapat memberi sinyal pekerjaan selesai melalui interrupt. Dengan MSI-X, sistem dapat merutekan completion queue berbeda ke interrupt vector berbeda, mendukung pemrosesan yang memiliki CPU affinity dan mengurangi ketergantungan pada satu jalur interrupt bersama.

Interrupt memungkinkan CPU melakukan pekerjaan lain atau masuk ke idle state ketika operasi storage sedang berjalan. Trade-off-nya adalah overhead delivery dan scheduling interrupt, yang dapat menjadi signifikan ketika perangkat cepat menyelesaikan request sangat kecil dengan laju tinggi.

Polling mengambil pendekatan lain. CPU berulang kali memeriksa completion queue entry alih-alih menunggu interrupt. Ini dapat mengurangi wake-up dan interrupt latency, tetapi mengonsumsi waktu CPU bahkan ketika hanya sedikit atau tidak ada pekerjaan yang selesai.

Storage stack berperforma tinggi dapat memakai polling secara selektif untuk workload sensitif latency. Sistem general-purpose umumnya memilih interrupt atau strategi hybrid karena efisiensi CPU penting bersama latency storage.

Struktur queue mendukung kedua pendekatan. Perbedaannya terutama terletak pada cara software menentukan kapan harus memeriksa completion ring.

Queue depth mengubah tekanan throughput dan latency

Mempertahankan beberapa command outstanding dapat menaikkan throughput karena controller memiliki lebih banyak pekerjaan untuk dijadwalkan. Jika satu operasi flash tertahan, operasi lain mungkin dapat memakai resource internal berbeda. Queue yang dalam juga dapat menjaga transfer PCIe dan pipeline controller tetap aktif.

Lebih banyak pekerjaan outstanding juga menciptakan waktu tunggu. Sebuah request dapat menunggu di belakang operasi sebelumnya di host, controller, atau media scheduler. Saat saturasi, menambah queue depth sering meningkatkan latency jauh sebelum meningkatkan throughput yang berguna.

Hal ini terutama terlihat pada tail latency. SSD dapat memberikan bandwidth agregat yang sangat tinggi sementara sebagian request menunggu jauh lebih lama selama burst, garbage collection, thermal throttling, atau contention antar namespace dan resource flash internal.

Tuning queue depth karena itu bergantung pada workload. Transfer sequential besar, database I/O, virtual machine, dan small random read yang sensitif latency memberi tuntutan berbeda pada controller.

Hasil benchmark pada queue depth 32 bukan prediksi langsung untuk workload interaktif yang biasanya hanya memiliki satu atau dua request outstanding.

NVMe queue tidak menghapus batas media flash

Arsitektur queue membuat transport command efisien, tetapi tidak dapat membuat NAND flash menjadi instan. Read, program operation, block erase, error correction, pekerjaan mapping table, garbage collection, dan wear management tetap menimbulkan biaya media dan firmware.

Controller dapat menerima command dengan cepat tetapi menyelesaikannya kemudian. Submission latency dan service latency merupakan bagian terpisah dari jalur tersebut.

Demikian pula, kapasitas queue besar yang diiklankan tidak berarti setiap command dalam queue dieksekusi secara fisik paralel. Controller menjadwalkan pekerjaan terhadap jumlah channel, die, internal buffer, bandwidth PCIe, dan resource firmware yang terbatas.

Pemisahan ini berguna saat mendiagnosis performa storage. Jika submission queue tetap murah tetapi completion latency naik tajam saat load meningkat, bottleneck mungkin berada setelah command diterima, bukan pada interface host-ke-controller.

NVMe queue pair menyediakan transport efisien antara software host dan storage controller paralel. Submission queue mengekspos pekerjaan yang menunggu, completion queue mengembalikan status, command identifier menghubungkan hasil dengan request, phase tag membuat penggunaan ulang ring aman, dan doorbell mengomunikasikan progres. Desain ini dapat diskalakan karena CPU dan controller dapat bergerak secara independen alih-alih mengubah setiap operasi storage menjadi pertukaran register sinkron.