Menyinkronkan Kode Interupsi
Faktor-faktor berikut mempersulit kode driver yang menangani gangguan perangkat keras pada sistem multiprosesor:
Setiap kali perangkat terganggu, perangkat memberikan informasi khusus interupsi yang mudah menguap karena dapat ditimpa saat berikutnya perangkat mengganggu.
Perangkat mengganggu IRQL yang relatif tinggi dan rutinitas layanan interupsi (ISR) mereka dapat mengganggu eksekusi kode driver lainnya.
Untuk gangguan DIRQL, ISR harus berjalan di DIRQL sambil menahan kunci putar yang disediakan pengemudi, sehingga ISR dapat mencegah gangguan tambahan saat menyimpan informasi yang mudah menguap. DIRQL mencegah gangguan oleh prosesor saat ini, dan kunci putar mencegah gangguan oleh prosesor lain.
ISR harus berjalan dengan cepat karena perangkat tidak dapat mengganggu saat ISR dijalankan. Waktu eksekusi ISR yang lama dapat memperlambat sistem atau mungkin menyebabkan kehilangan data.
Rutinitas ISR dan panggilan prosedur yang ditangguhkan (DPC) biasanya harus mengakses area penyimpanan tempat ISR menyimpan data volatil perangkat. Rutinitas ini harus disinkronkan satu sama lain sehingga mereka tidak mengakses area penyimpanan secara bersamaan.
Karena semua faktor ini, Anda harus menggunakan aturan berikut saat menulis kode driver yang menangani gangguan:
Hanya fungsi panggilan balik EvtInterruptIsr yang mengakses data gangguan volatil, seperti register perangkat yang berisi informasi interupsi.
Fungsi panggilan balik EvtInterruptIsr harus memindahkan data volatil ke buffer data interupsi yang ditentukan driver yang dapat diakses oleh fungsi panggilan balik EvtInterruptDpc driver, fungsi panggilan balik EvtInterruptWorkItem , atau beberapa fungsi panggilan balik EvtDpcFunc .
Jika driver Anda menyediakan fungsi panggilan balik EvtInterruptDpc atau EvtInterruptWorkItem untuk objek interupsinya, tempat terbaik untuk menyimpan data interupsi adalah ruang konteks objek yang mengganggu. Fungsi panggilan balik objek interupsi dapat mengakses ruang konteks objek dengan menggunakan handel objek yang mereka terima.
Jika driver Anda menyediakan beberapa fungsi panggilan balik EvtDpcFunc untuk setiap fungsi panggilan balik EvtInterruptIsr , Anda dapat menyimpan data interupsi di setiap ruang konteks objek DPC.
Semua kode driver yang mengakses buffer data interupsi harus disinkronkan sehingga hanya satu rutin mengakses data pada satu waktu.
Untuk objek interupsi DIRQL, fungsi panggilan balik EvtInterruptIsr mengakses buffer data ini di IRQL = DIRQL sambil menahan kunci putar yang disediakan driver objek interupsi. Oleh karena itu, semua rutinitas yang mengakses buffer juga harus berjalan di DIRQL sambil memegang kunci putar. (Biasanya, fungsi panggilan balik EvtInterruptDpc atau EvtDpcFunc interupsi adalah satu-satunya rutinitas lain yang harus mengakses buffer.)
Semua rutinitas yang mengakses buffer data interupsi, kecuali untuk fungsi panggilan balik EvtInterruptIsr , harus melakukan salah satu hal berikut:
- Panggil WdfInterruptSynchronize untuk menjadwalkan fungsi panggilan balik EvtInterruptSynchronize yang mengakses buffer data interupsi.
- Tempatkan kode yang mengakses buffer data interupsi antara panggilan ke WdfInterruptAcquireLock dan WdfInterruptReleaseLock.
Kedua teknik ini memungkinkan fungsi EvtInterruptDpc atau EvtDpcFunc untuk mengakses data interupsi di DIRQL sambil menahan kunci spin interupsi. DIRQL mencegah gangguan oleh prosesor saat ini, dan kunci putar mencegah gangguan oleh prosesor lain.
Jika perangkat Anda mendukung beberapa vektor atau pesan interupsi, dan jika Anda ingin menyinkronkan penanganan pengandar dari gangguan ini, Anda dapat menetapkan kunci putar tunggal ke beberapa objek gangguan DIRQL. Kerangka kerja menentukan DIRQL tertinggi dari set gangguan, dan selalu memperoleh kunci putar pada DIRQL tersebut sehingga kode yang disinkronkan tidak dapat terganggu oleh vektor atau pesan gangguan dalam set.
Untuk objek interupsi tingkat pasif, kerangka kerja memperoleh kunci gangguan tingkat pasif sebelum memanggil fungsi panggilan balik EvtInterruptIsr driver di IRQL = PASSIVE_LEVEL. Akibatnya, semua rutinitas yang mengakses buffer harus memperoleh kunci interupsi atau menyinkronkan akses buffer secara internal. Biasanya, fungsi panggilan balik EvtInterruptWorkItem interupsi adalah satu-satunya rutinitas lain yang mengakses buffer. Untuk informasi tentang memperoleh kunci interupsi dari fungsi panggilan balik EvtInterruptWorkItem , lihat bagian Keterangan di halaman tersebut.
Anda juga dapat menyinkronkan penanganan driver dari beberapa vektor interupsi dengan menetapkan satu kunci tunggu ke beberapa objek interupsi tingkat pasif.
Jika beberapa kode Anda yang menangani gangguan DIRQL harus berjalan di IRQL = PASSIVE_LEVEL, fungsi panggilan balik EvtInterruptDpc atau EvtDpcFunc Anda dapat membuat satu atau beberapa item kerja sehingga kode akan berjalan sebagai fungsi panggilan balik EvtWorkItem .
Atau, di KMDF versi 1.11 dan yang lebih baru, driver dapat meminta item kerja interupsi dengan memanggil WdfInterruptQueueWorkItemForIsr. (Ingat bahwa fungsi panggilan balik EvtInterruptIsr driver dapat memanggil WdfInterruptQueueWorkItemForIsr atau WdfInterruptQueueDpcForIsr, tetapi tidak keduanya.)
Jika penting untuk menyinkronkan fungsi panggilan balik EvtInterruptDpc dan EvtDpcFunc driver satu sama lain dan dengan fungsi panggilan balik lain yang terkait dengan perangkat, driver Anda dapat mengatur anggota AutomaticSerialization ke TRUE dalam struktur WDF_INTERRUPT_CONFIG interupsi dan struktur WDF_DPC_CONFIG objek DPC. Atau, driver dapat menggunakan kunci putar kerangka kerja. (Mengatur anggota AutomaticSerialization ke TRUE tidak menyinkronkan fungsi panggilan balik EvtInterruptIsr dengan fungsi panggilan balik lainnya. Gunakan WdfInterruptSynchronize atau WdfInterruptAcquireLock untuk menyinkronkan fungsi panggilan balik EvtInterruptIsr , seperti yang dijelaskan sebelumnya dalam topik ini.)
Untuk informasi selengkapnya tentang menyinkronkan rutinitas driver, lihat Teknik Sinkronisasi untuk Driver Framework-Based.