Bagaimana kernel Linux menangani IRQ yang dibagikan?

14

Menurut apa yang saya baca sejauh ini, "ketika kernel menerima interupsi, semua penangan terdaftar dipanggil."

Saya memahami bahwa penangan yang terdaftar untuk setiap IRQ dapat dilihat melalui /proc/interrupts, dan saya juga mengerti bahwa penangan yang terdaftar berasal dari driver yang telah memanggil request_irqlewat callback kira-kira dalam bentuk:

irqreturn_t (*handler)(int, void *)

Berdasarkan apa yang saya ketahui, masing-masing callback interrupt handler yang terkait dengan IRQ tertentu harus dipanggil, dan tergantung pada handler untuk menentukan apakah interupsi memang harus ditangani olehnya. Jika handler tidak menangani interupsi tertentu, ia harus mengembalikan makro kernel IRQ_NONE.

Yang saya mengalami kesulitan memahami adalah, bagaimana masing-masing pengemudi diharapkan untuk menentukan apakah harus menangani interupsi atau tidak. Saya kira mereka dapat melacak secara internal jika mereka mengharapkan gangguan. Jika demikian, saya tidak tahu bagaimana mereka dapat menangani situasi di mana beberapa driver di belakang IRQ yang sama mengharapkan interupsi.

Alasan saya mencoba memahami detail ini adalah karena saya mengacaukan kexecmekanisme untuk mengeksekusi ulang kernel di tengah operasi sistem sambil bermain dengan pin reset dan berbagai register pada PCIe bridge serta PCI downstream alat. Dan dengan melakukan itu, setelah reboot saya mendapatkan panik kernel, atau driver lain mengeluh bahwa mereka menerima interupsi meskipun tidak ada operasi yang terjadi.

Bagaimana pawang memutuskan bahwa interupsi harus ditangani olehnya adalah misteri.

Sunting: Dalam kasus itu relevan, arsitektur CPU yang dimaksud adalah x86.

bsirang
sumber
1
stackoverflow.com/questions/14371513/for-a-
Shared-interrupt-line-how-do-i-find-which-interrupt-handler-to-usec

Jawaban:

14

Ini dibahas dalam bab 10 dari Linux Device Drivers , edisi ke-3, oleh Corbet et al. Ini tersedia secara online gratis , atau Anda bisa melempar shekels O'Reilly untuk bentuk pohon mati atau bentuk ebook. Bagian yang relevan dengan pertanyaan Anda dimulai pada halaman 278 di tautan pertama.

Untuk apa nilainya, inilah upaya saya untuk memparafrasekan ketiga halaman tersebut, ditambah bit lain yang telah saya Google-Google:

  • Ketika Anda mendaftarkan penangan IRQ bersama, kernel memeriksa apakah:

    Sebuah. tidak ada pawang lain yang ada untuk gangguan itu, atau

    b. semua yang sebelumnya terdaftar juga meminta berbagi interupsi

    Jika salah satu kasus berlaku, itu kemudian memeriksa bahwa dev_idparameter Anda unik, sehingga kernel dapat membedakan beberapa penangan, misalnya selama penghapusan penangan.

  • Ketika perangkat keras PCI¹ mengangkat garis IRQ, penangan interupsi tingkat rendah dari kernel dipanggil, dan pada gilirannya memanggil semua penangan interupsi yang terdaftar, melewati masing-masing kembali yang dev_idAnda gunakan untuk mendaftarkan penangan melalui request_irq().

    The dev_idnilai perlu mesin-unik. Cara umum untuk melakukannya adalah dengan memberikan pointer ke per-perangkat yang structdigunakan driver Anda untuk mengelola perangkat itu. Karena penunjuk ini harus berada dalam ruang memori driver Anda agar berguna bagi driver, itu ipso facto unik untuk driver itu. ²

    Jika ada beberapa driver terdaftar untuk interupsi yang diberikan, mereka semua akan dipanggil ketika salah satu perangkat menaikkan garis interupsi bersama. Jika bukan perangkat driver Anda yang melakukan ini, pengendali interrupt driver Anda akan diberikan dev_idnilai yang bukan miliknya. Handler interrupt pengemudi Anda harus segera kembali ketika ini terjadi.

    Kasus lain adalah driver Anda mengelola banyak perangkat. Handler interrupt pengemudi akan mendapatkan salah satu dev_idnilai yang diketahui pengemudi. Kode Anda seharusnya polling setiap perangkat untuk mencari tahu mana yang mengangkat interupsi.

    Contoh Corbet et al. berikan adalah port paralel PC. Ketika menegaskan garis interupsi, itu juga menetapkan bit atas dalam register perangkat pertama. (Yaitu inb(0x378) & 0x80 == true, dengan asumsi penomoran port I / O standar.) Ketika pawang Anda mendeteksi hal ini, ia seharusnya melakukan tugasnya, lalu menghapus IRQ dengan menuliskan nilai yang dibaca dari port I / O kembali ke port dengan bagian atas. sedikit dibersihkan.

    Saya tidak melihat alasan mengapa mekanisme khusus itu istimewa. Perangkat perangkat keras yang berbeda dapat memilih mekanisme yang berbeda. Satu-satunya hal yang penting adalah bahwa perangkat mengizinkan interupsi bersama, itu harus memiliki beberapa cara bagi pengemudi untuk membaca status interupsi perangkat, dan beberapa cara untuk menghapus interupsi. Anda harus membaca lembar data atau panduan pemrograman perangkat Anda untuk mengetahui mekanisme apa yang digunakan perangkat khusus Anda.

  • Ketika interrupt handler memberitahu kernel yang menangani interupsi, itu tidak menghentikan kernel untuk terus memanggil penangan lain yang terdaftar untuk interupsi yang sama. Ini tidak dapat dihindari jika Anda ingin berbagi jalur interupsi saat menggunakan interupsi yang dipicu level.

    Bayangkan dua perangkat menegaskan garis interupsi yang sama pada saat yang sama. (Atau paling tidak, sangat dekat dalam waktu sehingga kernel tidak punya waktu untuk memanggil penangan interupsi untuk menghapus garis dan dengan demikian melihat pernyataan kedua sebagai terpisah.) Kernel harus memanggil semua penangan untuk garis interupsi itu, untuk memberikan masing-masing kesempatan untuk menanyakan perangkat keras terkait untuk melihat apakah perlu perhatian. Sangat mungkin bagi dua driver yang berbeda untuk berhasil menangani interupsi dalam pass yang sama melalui daftar handler untuk interupsi yang diberikan.

    Karena itu, sangat penting bahwa pengemudi Anda memberi tahu perangkat yang ia kelola untuk menghapus pernyataan interupsinya beberapa saat sebelum pengendali interupsi kembali. Tidak jelas bagi saya apa yang terjadi sebaliknya. Baris interupsi yang ditegaskan secara terus-menerus akan menghasilkan kernel yang terus-menerus memanggil penangan interupsi yang dibagikan, atau itu akan menutupi kemampuan kernel untuk melihat interupsi baru sehingga penangan tidak pernah dipanggil. Either way, bencana.


Catatan kaki:

  1. Saya menentukan PCI di atas karena semua di atas mengasumsikan gangguan yang dipicu tingkat , seperti yang digunakan dalam spesifikasi PCI asli. ISA menggunakan interupsi yang dipicu oleh tepi , yang membuat berbagi menjadi sangat sulit, dan bahkan mungkin hanya ketika didukung oleh perangkat keras. PCIe menggunakan interupsi sinyal-sinyal ; pesan interupsi berisi nilai unik yang dapat digunakan kernel untuk menghindari permainan tebak-tebakan yang dibutuhkan dengan PCI interrupt sharing. PCIe dapat menghilangkan kebutuhan yang sangat besar untuk berbagi interupsi. (Saya tidak tahu apakah itu benar-benar terjadi, hanya saja itu berpotensi.)

  2. Driver kernel Linux semuanya berbagi ruang memori yang sama, tetapi driver yang tidak terkait tidak seharusnya mucking di ruang memori orang lain. Kecuali Anda melewatkan pointer itu, Anda bisa yakin driver lain tidak akan muncul dengan nilai yang sama secara tidak sengaja sendiri.

Warren Young
sumber
1
Seperti yang Anda sebutkan, pengendali interupsi dapat dilewati dan dev_idbukan miliknya. Bagi saya kelihatannya ada kemungkinan non-nol bahwa seorang pengemudi yang tidak memiliki dev_idstruktur mungkin masih menganggapnya sebagai miliknya sendiri berdasarkan pada bagaimana ia menginterpretasikan konten. Jika ini bukan masalahnya, mekanisme apa yang akan mencegah hal ini?
bsirang
Anda mencegahnya dengan membuat dev_idpointer ke sesuatu di dalam ruang memori driver Anda. Pengemudi lain dapat membuat dev_idnilai yang kebetulan membingungkan dengan pointer ke memori yang dimiliki pengemudi Anda, tetapi itu tidak akan terjadi karena semua orang bermain sesuai aturan. Ini adalah ruang kernel, ingat: disiplin diri diasumsikan sebagai hal yang biasa, tidak seperti dengan kode ruang pengguna, yang mungkin dengan anggapan menganggap bahwa segala sesuatu yang tidak dilarang diperbolehkan.
Warren Young
Menurut bab sepuluh LDD3: "Setiap kali dua atau lebih driver berbagi jalur interupsi dan perangkat keras mengganggu prosesor pada jalur itu, kernel memanggil setiap handler yang terdaftar untuk interupsi itu, melewati masing-masing dev_id sendiri" Sepertinya pemahaman sebelumnya salah mengenai apakah interrupt handler dapat diberikan pada dev_idyang tidak dimilikinya.
bsirang
Itu salah baca dari saya. Ketika saya menulis itu, saya menggabungkan dua konsep. Saya sudah mengedit jawaban saya. Kondisi yang mengharuskan penangan interupsi Anda untuk kembali dengan cepat adalah bahwa ia dipanggil karena pernyataan interupsi oleh perangkat yang tidak dikelola. Nilai dev_idtidak membantu Anda menentukan apakah ini telah terjadi. Anda harus bertanya pada perangkat keras, "Anda menelepon?"
Warren Young
Ya, sekarang saya perlu mencari tahu bagaimana apa yang saya mainkan sebenarnya menyebabkan driver lain percaya perangkat mereka "berdering" setelah restart kernel via kexec.
bsirang
4

Ketika driver meminta IRQ yang dibagikan, ia mengirimkan pointer ke kernel ke referensi ke struktur spesifik perangkat dalam ruang memori driver.

Menurut LDD3:

Setiap kali dua atau lebih driver berbagi jalur interupsi dan perangkat keras menginterupsi prosesor pada jalur itu, kernel memanggil setiap handler yang terdaftar untuk interupsi itu, melewati masing-masing dev_id sendiri.

Setelah memeriksa penangan IRQ beberapa driver, tampaknya mereka menyelidiki perangkat keras itu sendiri untuk menentukan apakah harus menangani interupsi atau kembali IRQ_NONE.

Contohnya

Driver UHCI-HCD
  status = inw(uhci->io_addr + USBSTS);
  if (!(status & ~USBSTS_HCH))  /* shared interrupt, not mine */
    return IRQ_NONE;

Dalam kode di atas, pengemudi membaca USBSTSregister untuk menentukan apakah ada gangguan pada layanan.

Driver SDHCI
  intmask = sdhci_readl(host, SDHCI_INT_STATUS);

  if (!intmask || intmask == 0xffffffff) {
    result = IRQ_NONE;
    goto out;
  }

Sama seperti pada contoh sebelumnya, pengemudi sedang memeriksa register status, SDHCI_INT_STATUSuntuk menentukan apakah perlu layanan interupsi.

Pengemudi Ath5k
  struct ath5k_softc *sc = dev_id;
  struct ath5k_hw *ah = sc->ah;
  enum ath5k_int status;
  unsigned int counter = 1000;

  if (unlikely(test_bit(ATH_STAT_INVALID, sc->status) ||
        !ath5k_hw_is_intr_pending(ah)))
    return IRQ_NONE;

Hanya satu contoh lagi.

bsirang
sumber
0

Kunjungi kunjungi tautan ini :

Ini adalah praktik yang biasa untuk memicu bagian bawah atau logika lain di penangan IRQ hanya setelah memeriksa status IRQ dari register yang dipetakan memori. Oleh karena itu masalah ini diselesaikan secara default oleh programmer yang baik.

Priyaranjan
sumber
Konten tautan Anda tidak tersedia
user3405291