Completion Queue NVMe adalah array melingkar berukuran tetap di memori host. Controller menulis entri completion ketika command selesai, sedangkan software host mengonsumsi entri tersebut dan memajukan head queue. Pada akhirnya, kedua sisi kembali ke slot yang masih menyimpan data dari putaran ring sebelumnya.

Penggunaan ulang memori menimbulkan ambiguitas kecil tetapi penting. Sebuah slot dapat berisi completion entry yang formatnya valid walaupun controller belum menulis completion baru ke posisi itu. Mengosongkan setiap entri yang sudah dikonsumsi akan menambah traffic memori dan tetap memerlukan koordinasi yang tepat antara host dan controller.

NVMe mengatasi ambiguitas tersebut dengan phase tag yang dibawa setiap Completion Queue entry. Tag berubah ketika controller melakukan wrap dari slot terakhir kembali ke slot pertama. Software host menyimpan phase yang diharapkan, sehingga slot fisik yang sama dapat digunakan berulang kali tanpa perlu menghapus isi lamanya terlebih dahulu.

Completion Queue digunakan ulang di tempat yang sama

NVMe memisahkan pengiriman command dari penyelesaian command. Software host menempatkan command ke Submission Queue, memperbarui tail doorbell terkait, lalu controller menulis hasilnya ke Completion Queue yang terasosiasi.

Completion Queue memiliki jumlah entri tetap yang ditentukan saat queue dibuat. Setiap completion entry memuat field seperti Submission Queue identifier, command identifier, pointer head Submission Queue, dan informasi status. Queue tidak bertambah panjang ketika completion terakumulasi.

Controller menangani sisi producer pada Completion Queue. Software host menangani sisi consumer. Setelah memproses satu atau beberapa completion entry, host memperbarui Completion Queue head doorbell agar controller dapat menggunakan ulang posisi yang sudah dilepas.

Karena penyimpanannya berbentuk ring, slot nol tidak terikat permanen pada command pertama. Slot itu dapat memuat banyak completion berbeda selama umur queue. Hal yang sama berlaku untuk seluruh slot lain.

Penggunaan ulang ini efisien: ukuran memori queue tetap terbatas dan dapat terus dipetakan untuk DMA. Konsekuensinya, byte yang sudah ada dalam sebuah slot tidak dapat menjadi bukti bahwa entri tersebut masih baru.

Data completion lama tetap dapat terlihat valid

Bayangkan Completion Queue dengan empat entri. Dalam satu putaran, controller mengisi entri 0 sampai 3 dan host mengonsumsinya. Host memajukan head ketika memproses setiap entri, tetapi byte lama dapat tetap berada di memori.

Saat controller memulai putaran berikutnya, entri 0 masih memuat completion sebelumnya sampai completion baru menimpanya. Jika host menentukan freshness hanya dari field seperti command identifier atau status code, data lama dapat keliru dianggap sebagai hasil yang baru diposting.

Mengisi entri yang sudah dikonsumsi dengan nol bukan bagian dari protokol. Entri bernilai nol juga membutuhkan aturan terpisah untuk menentukan apakah nol berarti kosong atau merupakan nilai field yang sah. Selain itu, controller dan host tetap membutuhkan penanda ownership yang ringkas untuk penggunaan ring berulang.

Phase tag menyediakan penanda tersebut tanpa membutuhkan validity array kedua atau proses pembersihan terhadap slot yang sudah dikonsumsi.

Phase bit berubah ketika queue melakukan wrap

Phase Tag merupakan bagian dari field status completion. Dalam satu putaran Completion Queue, entri baru membawa satu nilai phase. Ketika controller melewati entri terakhir dan kembali ke entri pertama, nilai phase untuk completion berikutnya dibalik.

Software host menyimpan nilai phase yang diharapkan untuk queue tersebut. Pada posisi head saat ini, completion dengan phase yang cocok dapat diperlakukan sebagai entri yang baru diposting. Phase yang tidak cocok berarti controller belum menghasilkan completion untuk posisi tersebut pada putaran host saat ini.

Ketika head milik host melewati slot terakhir dan kembali ke slot nol, host juga membalik nilai phase yang diharapkannya. Entri lama di slot nol masih membawa phase dari putaran sebelumnya sampai controller menimpanya. Karena itu, entri lama tersebut gagal pada pemeriksaan phase yang baru.

Setelah satu putaran penuh lagi, phase bit kembali ke nilai sebelumnya. Hal ini tidak menimbulkan ambiguitas karena protokol queue juga membatasi pergerakan producer dan consumer. Penanda satu bit cukup ketika dibaca bersama posisi pada circular queue dan aturan bahwa controller tidak boleh menimpa entri yang belum dikonsumsi.

Tag adalah penanda ownership, bukan penghitung completion

Phase tag hanya memiliki dua nilai. Tag tersebut tidak mengidentifikasi command, menghitung request yang selesai, atau menyimpan jumlah wrap yang sudah terjadi. Fungsi itu ditangani oleh state queue dan field completion lainnya.

Command identifier menghubungkan completion dengan command pada Submission Queue. Submission Queue identifier menunjukkan queue asalnya. Head Completion Queue milik host menunjukkan posisi konsumsi. Phase tag menjawab pertanyaan yang lebih sempit: apakah entri pada head saat ini berasal dari putaran yang sedang dikonsumsi host.

Pemisahan fungsi ini penting dalam implementasi. Menganggap phase bit sebagai sequence number akan memberikan makna yang tidak dimilikinya. Menganggap command identifier yang cocok sebagai bukti freshness juga tidak aman karena command identifier dapat digunakan ulang setelah command sebelumnya selesai.

Pemeriksaan yang tepat bergantung pada konteks: baca entri pada head Completion Queue saat ini lalu bandingkan phase-nya dengan phase yang diharapkan untuk posisi queue tersebut.

Queue depth menentukan frekuensi wrap

Completion Queue yang lebih kecil mencapai ujungnya lebih sering pada completion rate yang sama, sehingga phase lebih sering berganti. Queue yang lebih besar melakukan wrap lebih jarang. Keduanya tidak mengubah arti tag.

Queue depth juga memengaruhi jumlah completion yang dapat menunggu sebelum konsumsi oleh host menjadi batas. Controller tidak boleh menimpa completion entry yang belum dilepas host melalui pergerakan head.

Hal ini terpisah dari mekanisme phase. Tag membedakan isi slot lama dari completion yang diposting untuk putaran saat ini; tag tidak memberikan izin untuk menimpa entri yang masih menjadi milik host.

Completion Queue head doorbell karena itu menjadi bagian dari batas pengelolaan resource. Doorbell memberi tahu controller sejauh mana host telah mengonsumsi ring. Pemeriksaan phase memberi tahu host apakah posisi berikutnya berisi completion dari putaran yang diharapkan.

Memory ordering tetap penting saat melakukan polling queue

Phase tag menyelesaikan ambiguitas generasi slot, tetapi tidak menggantikan aturan DMA dan memory ordering pada platform. Driver harus menggunakan primitive akses dan aturan ordering yang diwajibkan sistem operasi serta arsitekturnya ketika membaca memori yang ditulis perangkat.

Kode polling biasanya memeriksa phase pada head saat ini sebelum mengonsumsi bagian lain dari completion. Pola akses yang tepat harus mengikuti jaminan dari interface NVMe, jalur PCIe, arsitektur CPU, dan API DMA kernel yang digunakan.

Perbedaan ini penting karena phase bit adalah field protokol, bukan memory barrier CPU yang berlaku universal. Melihat nilai bit tertentu tidak memberi izin kepada software untuk mengabaikan aturan akses memori pada platform.

Karena itu, kode driver perlu menggabungkan semantik queue NVMe dengan primitive device-memory dan DMA yang sudah ditetapkan sistem operasi, bukan membuat asumsi ordering hanya dari mekanisme phase.

Interrupt tidak menghapus kebutuhan pemeriksaan phase

NVMe dapat memberi sinyal aktivitas completion melalui interrupt, dan implementasi juga dapat melakukan polling terhadap Completion Queue. Interrupt menunjukkan bahwa pekerjaan completion mungkin tersedia; sinyal tersebut tidak mengubah queue menjadi struktur data lain.

Beberapa completion dapat sudah tersedia ketika software mulai memproses queue, dan interrupt coalescing dapat mengubah frekuensi notifikasi ke host. Consumer tetap membutuhkan kondisi berhenti yang andal saat berjalan sepanjang ring.

Expected phase menyediakan batas tersebut. Software dapat mengonsumsi entri dengan phase yang cocok secara berurutan dari head saat ini dan berhenti ketika entri berikutnya membawa phase yang berlawanan. Setelah itu, software melaporkan posisi yang sudah dikonsumsi dengan memajukan Completion Queue head doorbell.

Aturan queue yang sama berlaku untuk busy polling, jalur berbasis interrupt, maupun desain hybrid. Kebijakan notifikasi mengubah waktu software memeriksa queue, sedangkan phase tag tetap menjadi bagian dari penentuan entri ring yang sedang berlaku.

Penggunaan ulang ring tetap terbatas tanpa mengosongkan slot

Mekanisme phase ringkas karena memakai state yang sudah dibawa setiap completion serta satu expected bit dalam state queue milik host. Tidak diperlukan operasi pembersihan per entri setelah konsumsi, dan tidak diperlukan generation number yang terus meningkat pada setiap slot.

Cakupannya sengaja sempit. Mekanisme ini tidak menggantikan command identifier, pointer queue, doorbell, penanganan interrupt, atau DMA ordering. Fungsinya adalah membuat penggunaan berulang terhadap memori Completion Queue yang sama tetap tidak ambigu pada posisi consumer saat ini.

Field kecil tersebut memungkinkan ring mempertahankan byte lama tanpa membuatnya tampak sebagai data terkini. Ketika queue melakukan wrap, arti slot mengikuti expected phase, sehingga host dan controller dapat menggunakan memori yang sama secara terus-menerus sambil tetap memisahkan completion baru dari isi putaran sebelumnya.