Sinkronisasi dan Pemberitahuan di Driver Jaringan

Setiap kali dua utas eksekusi berbagi sumber daya yang dapat diakses pada saat yang sama, baik di komputer uniprosesor atau pada komputer multiprosesor simetris (SMP), mereka perlu disinkronkan. Misalnya, pada komputer uniprosesor, jika satu fungsi driver mengakses sumber daya bersama dan terganggu oleh fungsi lain yang berjalan di IRQL yang lebih tinggi, seperti ISR, sumber daya bersama harus dilindungi untuk mencegah kondisi ras yang meninggalkan sumber daya dalam keadaan tidak ditentukan. Pada komputer SMP, dua utas dapat berjalan secara bersamaan pada prosesor yang berbeda dan mencoba memodifikasi data yang sama. Akses tersebut harus disinkronkan.

NDIS menyediakan kunci spin yang dapat Anda gunakan untuk menyinkronkan akses ke sumber daya bersama antara utas yang berjalan pada IRQL yang sama. Ketika dua utas yang berbagi sumber daya berjalan di IRQL yang berbeda, NDIS menyediakan mekanisme untuk menaikkan IRQL kode IRQL yang lebih rendah untuk sementara waktu sehingga akses ke sumber daya bersama dapat diserialisasikan.

Ketika utas bergantung pada terjadinya peristiwa di luar utas, utas bergantung pada pemberitahuan. Misalnya, driver mungkin perlu diberi tahu ketika beberapa periode waktu telah berlalu sehingga dapat memeriksa perangkatnya. Atau driver kartu antarmuka jaringan (NIC) mungkin harus melakukan operasi berkala seperti polling. Timer menyediakan mekanisme seperti itu.

Peristiwa menyediakan mekanisme yang dapat digunakan oleh dua utas eksekusi untuk menyinkronkan operasi. Misalnya, driver miniport dapat menguji gangguan pada NIC dengan menulis ke perangkat. Driver harus menunggu interupsi untuk memberi tahu driver bahwa operasi berhasil. Anda dapat menggunakan peristiwa untuk menyinkronkan operasi antara utas yang menunggu interupsi selesai dan utas yang menangani gangguan.

Subbagian berikut dalam topik ini menjelaskan mekanisme NDIS ini.

Kunci Putar

Kunci putaran menyediakan mekanisme sinkronisasi untuk melindungi sumber daya yang dibagikan oleh utas mode kernel yang berjalan di IRQL > PASSIVE_LEVEL baik di uniprocessor atau komputer multiprosedor. Kunci putar menangani sinkronisasi di antara berbagai utas eksekusi yang berjalan bersamaan pada komputer SMP. Utas memperoleh kunci putaran sebelum mengakses sumber daya yang dilindungi. Kunci putaran menyimpan utas apa pun tetapi yang memegang kunci putaran dari menggunakan sumber daya. Pada komputer SMP, utas yang menunggu pada loop kunci putaran mencoba memperoleh kunci putaran sampai dilepaskan oleh utas yang memegang kunci.

Karakteristik lain dari kunci putar adalah IRQL terkait. Percobaan akuisisi kunci putaran untuk sementara meningkatkan IRQL dari utas yang diminta ke IRQL yang terkait dengan kunci putar. Ini mencegah semua utas IRQL yang lebih rendah pada prosesor yang sama mendahului utas yang dieksekusi. Utas, pada prosesor yang sama, berjalan pada IRQL yang lebih tinggi dapat mendahului utas yang dieksekusi, tetapi utas ini tidak dapat memperoleh kunci spin karena memiliki IRQL yang lebih rendah. Oleh karena itu, setelah utas memperoleh kunci putaran, tidak ada utas lain yang dapat memperoleh kunci putaran sampai telah dilepaskan. Driver jaringan yang ditulis dengan baik meminimalkan jumlah waktu kunci putaran ditahan.

Penggunaan umum untuk kunci putar adalah untuk melindungi antrean. Misalnya, fungsi pengiriman driver miniport, MiniportSendNetBufferLists, mungkin mengantre paket yang diteruskan ke dalamnya oleh driver protokol. Karena fungsi driver lain juga menggunakan antrean ini, MiniportSendNetBufferLists harus melindungi antrean dengan kunci putaran sehingga hanya satu utas pada satu waktu yang dapat memanipulasi tautan atau konten. MiniportSendNetBufferLists memperoleh kunci putar, menambahkan paket ke antrean lalu melepaskan kunci putar. Menggunakan kunci putaran memastikan bahwa utas yang memegang kunci adalah satu-satunya utas yang memodifikasi tautan antrean saat paket ditambahkan dengan aman ke antrean. Ketika driver miniport melepaskan paket dari antrean, akses seperti itu dilindungi oleh kunci putaran yang sama. Saat menjalankan instruksi yang memodifikasi kepala antrean atau salah satu bidang tautan yang membentuk antrean, driver harus melindungi antrean dengan kunci putar.

Pengemudi harus berhati-hati untuk tidak melindungi antrean secara berlebihan. Misalnya, driver dapat melakukan beberapa operasi (misalnya, mengisi bidang yang berisi panjangnya) di bidang paket yang dicadangkan driver jaringan sebelum mengantre paket. Driver dapat melakukan ini di luar wilayah kode yang dilindungi oleh kunci putaran, tetapi harus melakukannya sebelum mengantre paket. Setelah paket berada di antrean dan utas yang berjalan melepaskan kunci putaran, driver harus berasumsi bahwa utas lain dapat segera menghapus antrean paket.

Menghindari Masalah Spin Lock

Untuk menghindari kemungkinan kebuntuan, driver NDIS harus melepaskan semua kunci putar NDIS sebelum memanggil fungsi NDIS selain fungsi Spinlock NdisXxx. Jika driver NDIS tidak mematuhi persyaratan ini, kebuntuan dapat terjadi sebagai berikut:

  1. Thread 1, yang memegang NDIS spin lock A, memanggil fungsi NdisXxx yang mencoba memperoleh NDIS spin lock B dengan memanggil fungsi NdisAcquireSpinLock .

  2. Thread 2, yang memegang NDIS spin lock B, memanggil fungsi NdisXxx yang mencoba memperoleh NDIS spin lock A dengan memanggil fungsi NdisAcquireSpinLock .

  3. Utas 1 dan utas 2, yang masing-masing menunggu yang lain melepaskan kunci putarannya, menjadi kebuntuan.

Sistem operasi Microsoft Windows tidak membatasi driver jaringan secara bersamaan menahan lebih dari satu kunci putaran. Namun, jika satu bagian pengemudi mencoba memperoleh kunci spin A sambil memegang kunci spin B, dan bagian lain mencoba memperoleh kunci spin B sambil memegang kunci spin A, hasil kebuntuan. Jika memperoleh lebih dari satu kunci putaran, pengemudi harus menghindari kebuntuan dengan memberlakukan urutan akuisisi. Artinya, jika pengemudi memberlakukan perolehan spin lock A sebelum spin lock B, situasi yang dijelaskan di atas tidak akan terjadi.

Memperoleh kunci putaran akan meningkatkan IRQL ke DISPATCH_LEVEL dan menyimpan IRQL lama di kunci spin. Melepaskan kunci putaran mengatur IRQL ke nilai yang disimpan di kunci putar. Karena NDIS terkadang memasukkan driver pada PASSIVE_LEVEL, masalah dapat muncul dengan urutan kode berikut:

NdisAcquireSpinLock(A);
NdisAcquireSpinLock(B);
NdisReleaseSpinLock(A);
NdisReleaseSpinLock(B);

Driver tidak boleh mengakses kunci putaran dalam urutan ini karena alasan berikut:

  • Antara melepaskan spin lock A dan melepaskan spin lock B, kode berjalan pada PASSIVE_LEVEL alih-alih DISPATCH_LEVEL dan mengalami gangguan yang tidak pantas.

  • Setelah merilis kunci putar B, kode berjalan pada DISPATCH_LEVEL yang dapat menyebabkan pemanggil rusak di lain waktu dengan kesalahan IRQL_NOT_LESS_OR_EQUAL berhenti.

Menggunakan kunci spin berdampak pada performa dan, secara umum, pengemudi tidak boleh menggunakan banyak kunci putaran. Terkadang, fungsi yang biasanya berbeda (misalnya, fungsi kirim dan terima) memiliki tumpang tindih kecil yang dua kunci putarannya dapat digunakan. Penggunaan lebih dari satu kunci spin mungkin merupakan tradeoff yang berharga untuk memungkinkan kedua fungsi beroperasi secara independen pada prosesor terpisah.

Timer

Timer digunakan untuk operasi polling atau waktu habis. Driver membuat timer dan mengaitkan fungsi dengan timer. Fungsi terkait dipanggil ketika periode yang ditentukan dalam timer kedaluwarsa. Timer dapat berupa satu bidikan atau berkala. Setelah timer berkala ditetapkan, timer akan terus aktif pada kedaluwarsa setiap periode hingga dibersihkan secara eksplisit. Timer satu tembakan harus direset setiap kali ditembakkan.

Timer dibuat dan diinisialisasi dengan memanggil NdisAllocateTimerObject dan diatur dengan memanggil NdisSetTimerObject. Jika timer nonperiodic digunakan, timer harus direset dengan memanggil NdisSetTimerObject. Timer dibersihkan dengan memanggil NdisCancelTimerObject.

Acara

Peristiwa digunakan untuk menyinkronkan operasi antara dua utas eksekusi. Peristiwa dialokasikan oleh driver dan diinisialisasi dengan memanggil NdisInitializeEvent. Utas yang berjalan di IRQL = PASSIVE_LEVEL memanggil NdisWaitEvent untuk menempatkan dirinya ke dalam status tunggu. Saat utas driver menunggu peristiwa, alur menentukan waktu maksimum untuk menunggu serta peristiwa yang akan ditunggu. Penantian utas terpenuhi ketika NdisSetEvent disebut menyebabkan peristiwa diberi sinyal, atau ketika interval waktu tunggu maksimum yang ditentukan kedaluwarsa, mana pun yang terjadi terlebih dahulu.

Biasanya, acara ini ditetapkan oleh utas yang bekerja sama yang memanggil NdisSetEvent. Peristiwa tidak ditandatangani saat dibuat dan harus diatur untuk memberi sinyal utas tunggu. Peristiwa tetap disinyalkan sampai NdisResetEvent dipanggil.

Dukungan Multiproscessor di Driver Jaringan