PCIe Posted Write Memisahkan Penyelesaian CPU dari Visibilitas Perangkat
Sebuah MMIO store dapat selesai dari sudut pandang CPU ketika write terkait masih bergerak melalui jalur I/O. PCI dan PCIe memory write umumnya bersifat posted: requester tidak menunggu completion response untuk setiap write. Bridge dan logika interconnect dapat menerima transaction lalu membiarkan CPU melanjutkan eksekusi sebelum endpoint mengonsumsinya.
Perilaku ini berguna untuk throughput, tetapi menciptakan boundary yang tidak dapat diperlakukan driver seperti memori cache biasa. Program order pada CPU, ordering yang diberikan accessor MMIO, dan bukti bahwa perangkat telah menerima write merupakan properti yang saling berkaitan, tetapi tidak dapat saling menggantikan.
Posted write tidak memiliki completion response untuk ditunggu
PCIe mengklasifikasikan Memory Write Request biasa sebagai posted request. Requester mengirim transaction tanpa menerima Completion TLP yang mengonfirmasi bahwa endpoint telah memprosesnya. Memory Read Request mengikuti jalur berbeda: request ini bersifat non-posted dan memerlukan completion yang membawa status serta, untuk read yang berhasil, data.
Perbedaan tersebut penting ketika software memerlukan titik saat write ke control register sebelumnya sudah pasti mencapai perangkat. Kembalinya eksekusi dari instruksi CPU atau accessor MMIO write pada platform tidak dengan sendirinya menciptakan checkpoint yang membuktikan visibilitas pada endpoint.
Jalurnya dapat memiliki beberapa tahap:
CPU
|
MMIO accessor
|
host bridge / root complex
|
PCIe fabric
|
endpoint registerSebuah posted write dapat sudah diterima komponen upstream tetapi masih tertunda lebih jauh di downstream. Lokasi buffering dan timing yang tepat bergantung pada implementasi. Software seharusnya mengikuti kontrak ordering platform dan bus, bukan mengasumsikan keberadaan queue tertentu di dalam bridge.
MMIO ordering dan penyelesaian write menangani masalah berbeda
Linux menyediakan readl(), writel(), dan accessor terkait agar driver portabel tidak mengekspresikan I/O perangkat sebagai dereference pointer biasa. Accessor tersebut membawa semantik spesifik arsitektur untuk MMIO ordering dan interaksinya dengan memori normal.
Ordering menjawab hal seperti apakah satu akses perangkat dapat mendahului akses lain. Completion menjawab syarat yang lebih kuat: apakah write sebelumnya sudah bergerak sampai titik yang diperlukan driver sebelum eksekusi melewati boundary sensitif?
Barrier untuk memori normal bukan pengganti universal bagi operasi penyelesaian I/O. CPU memory ordering dapat membatasi saat operasi diterbitkan atau terlihat pada boundary prosesor tanpa memaksa sebuah posted PCIe write bergerak sepenuhnya sampai target. Primitive yang diperlukan bergantung pada arsitektur, mapping attribute, bus, dan kontrak driver.
Pembedaan ini juga memisahkan dua pola umum. Sebuah driver mungkin memerlukan ordering antara descriptor write di RAM dan MMIO doorbell. Pada jalur lain, driver mungkin memerlukan bukti bahwa write ke register penonaktifan interrupt telah mencapai perangkat sebelum fungsi kembali. Keduanya melibatkan ordering, tetapi kasus kedua menambahkan kebutuhan completion yang terlihat oleh perangkat.
Readback dapat membentuk checkpoint yang terlihat oleh perangkat
Dokumentasi kernel Linux menjelaskan teknik standar untuk kondisi yang mengharuskan posted write mencapai perangkat PCI: lakukan read dari perangkat yang sama setelah write. Read tidak dapat selesai sebelum persyaratan ordering yang relevan terhadap write sebelumnya terpenuhi, sehingga read yang kembali menjadi checkpoint bagi posted access sebelumnya.
Urutan sederhananya seperti berikut:
writel(mask, regs + IRQ_MASK);
(void)readl(regs + IRQ_MASK);Nilai hasil read dapat tidak relevan ketika tujuannya adalah melakukan flush terhadap write sebelumnya. Register yang dipilih untuk readback tetap penting. Driver memerlukan lokasi yang aman dibaca dalam state perangkat yang sedang ditangani.
Jalur reset dan removal memperjelas constraint tersebut. Register perangkat biasa mungkin tidak lagi merespons secara andal ketika hardware sedang di-reset. Dokumentasi Linux mencatat bahwa PCI configuration space dapat digunakan pada kondisi ketika flush read mungkin menghadapi perangkat yang tidak merespons, karena platform dapat menyediakan penanganan kegagalan yang terdefinisi untuk akses tersebut.
Readback bukan sinkronisasi dekoratif. Ia merupakan transaction I/O yang dipilih karena completion-nya menciptakan titik observasi yang tidak diberikan oleh posted write saja.
Spinlock tidak otomatis menguras jalur I/O
Lock melakukan serialisasi critical section pada software, tetapi masa hidup posted transaction dapat melampaui critical section kecuali aturan I/O platform menyediakan ordering yang dibutuhkan pada boundary tersebut.
Pertimbangkan dua CPU yang memperbarui register perangkat yang sama di bawah satu lock:
CPU 0: lock
CPU 0: MMIO write A
CPU 0: unlock
CPU 1: lock
CPU 1: MMIO write B
CPU 1: unlockUrutan lock pada source code tidak ambigu. Urutan yang terlihat perangkat tetap bergantung pada jaminan MMIO ordering dari arsitektur dan accessor. Linux memiliki mekanisme I/O ordering eksplisit untuk platform yang memerlukan penanganan tambahan terhadap posted write di sekitar boundary semacam ini.
Dokumentasi Linux saat ini menetapkan jaminan ordering yang cukup kuat bagi accessor generik readX() dan writeX() pada default I/O mapping, termasuk ordering antara write yang dilindungi spinlock yang sama. Driver yang memakai relaxed accessor, mapping attribute khusus, primitive spesifik arsitektur, atau asumsi lama harus mengikuti kontrak interface tersebut dan tidak memindahkan jaminan dari satu keluarga accessor ke keluarga lain.
Aturannya spesifik: sinkronisasi yang melindungi state software dan sinkronisasi yang membatasi I/O perangkat masing-masing harus memenuhi kontrak interface-nya sendiri.
Doorbell memperlihatkan boundary antara RAM dan MMIO
Perangkat berbasis queue sering mengambil descriptor dari system memory setelah software menulis doorbell register. Urutan yang dimaksud secara konseptual sederhana:
tulis field descriptor di RAM
publikasikan state descriptor
tulis MMIO doorbell
perangkat mengambil descriptorCorrectness bergantung pada jaminan DMA dan MMIO ordering yang menghubungkan langkah tersebut. Jika perangkat dapat melihat doorbell sebelum isi descriptor terlihat melalui jalur DMA, perangkat dapat mengambil state lama atau state yang belum dipublikasikan sepenuhnya.
Accessor MMIO standar Linux memberikan hubungan yang terdefinisi dengan memory write sebelumnya untuk default mapping yang didukung, sedangkan DMA API menetapkan aturan tambahan bagi coherent dan streaming mapping. Driver tidak dapat mengganti kontrak tersebut dengan asumsi umum bahwa urutan source code mencapai setiap observer tanpa perubahan.
Penyelesaian posted write menambahkan dimensi lain. Driver yang hanya membunyikan doorbell sering tidak perlu menunggu endpoint mengonsumsi doorbell; posting berguna justru karena eksekusi dapat berlanjut. Jalur yang menonaktifkan aktivitas hardware lalu melepaskan resource mungkin memerlukan checkpoint yang lebih kuat. Sinkronisasi yang dibutuhkan mengikuti semantik lifetime operasi, bukan kemiripan visual kedua register write tersebut.
Readback tambahan memiliki biaya nyata
Posted write memungkinkan jalur sisi CPU menghindari penantian round trip. Menambahkan read setelah setiap MMIO write akan mengurangi sebagian keuntungan tersebut. Read request memerlukan completion, sehingga requester harus menunggu response sebelum read itu sendiri selesai.
Hal ini tidak menghasilkan satu angka latency yang portabel. Biayanya bergantung pada arsitektur prosesor, host bridge, topologi, perangkat, virtualization layer, dan state sistem saat itu. Properti yang dapat dinyatakan secara defensif bersifat struktural: dependent readback menambahkan pekerjaan completion yang tidak diperlukan posted write.
Karena itu, driver menempatkan flush saat correctness membutuhkannya, bukan setelah setiap pembaruan register. Control transition, interrupt masking, reset sequencing, dan teardown merupakan lokasi yang layak diperiksa karena software dapat melintasi boundary lifetime atau ownership segera setelah write.
Jalur yang sensitif terhadap performance memerlukan ketelitian yang sama. Menghapus readback hanya aman ketika kontrak ordering yang tersisa masih mencakup operasi tersebut. Peningkatan benchmark tidak dapat membuktikan correctness jika pengujian tidak pernah memicu race yang sebelumnya dicegah readback.
State yang terlihat perangkat memiliki boundary sinkronisasi sendiri
Posted write memisahkan submission dari observasi endpoint. Pemisahan tersebut bukan cacat pada PCIe; ia merupakan bagian dari transaction model yang memungkinkan traffic write berjalan tanpa completion response untuk setiap request.
Kode driver menjadi rapuh ketika beberapa boundary dianggap sebagai satu hal. Urutan eksekusi CPU saja tidak membuktikan visibilitas endpoint. Memory barrier tidak secara inheren menguras posted I/O transaction. Lock tidak menggantikan jaminan MMIO dari platform. Sebaliknya, readback tidak diperlukan ketika driver hanya membutuhkan ordering yang lebih lemah dan sudah disediakan accessor.
Titik desain praktisnya adalah menentukan boundary yang benar-benar harus dilintasi operasi. Jika software hanya perlu mengirim pembaruan register, posting dapat tetap asynchronous. Jika kode berikutnya mensyaratkan perangkat telah menerima pembaruan tersebut, driver memerlukan mekanisme penyelesaian I/O yang didefinisikan untuk perangkat dan platform itu.