เคอร์เนลของ Linux จัดการ IRQ ที่แชร์ได้อย่างไร


14

ตามที่ฉันได้อ่านจนถึง "เมื่อเคอร์เนลได้รับการขัดจังหวะตัวจัดการที่ลงทะเบียนทั้งหมดจะถูกเรียกใช้"

ฉันเข้าใจว่าตัวจัดการที่ลงทะเบียนสำหรับ IRQ แต่ละตัวสามารถดูได้ผ่านทาง/proc/interruptsและฉันยังเข้าใจว่าตัวจัดการที่ลงทะเบียนนั้นมาจากไดรเวอร์ที่เรียกใช้การrequest_irqส่งผ่านในรูปแบบการติดต่อกลับโดยประมาณ:

irqreturn_t (*handler)(int, void *)

จากสิ่งที่ฉันรู้ควรเรียกใช้ตัวจัดการอินเทอร์รัปต์เหล่านี้ที่เกี่ยวข้องกับ IRQ เฉพาะและขึ้นอยู่กับตัวจัดการเพื่อพิจารณาว่าควรจัดการอินเทอร์รัปต์จริงหรือไม่ IRQ_NONEถ้าจัดการไม่ควรจัดการกับการขัดจังหวะโดยเฉพาะอย่างยิ่งจะต้องกลับแมโครเคอร์เนล

สิ่งที่ฉันมีปัญหาในการทำความเข้าใจคือคาดว่าไดรเวอร์แต่ละตัวจะตัดสินว่าควรจัดการกับอินเทอร์รัปต์หรือไม่ ฉันคิดว่าพวกเขาสามารถติดตามภายในถ้าพวกเขาควรจะคาดหวังว่าการขัดจังหวะ ถ้าเป็นเช่นนั้นฉันไม่รู้ว่าพวกเขาจะสามารถรับมือกับสถานการณ์ที่ไดรเวอร์หลายตัวที่อยู่เบื้องหลัง IRQ เดียวกันคาดว่าจะเกิดการขัดจังหวะได้อย่างไร

เหตุผลที่ฉันพยายามเข้าใจรายละเอียดเหล่านี้ก็เพราะฉันยุ่งกับkexecกลไกในการเรียกใช้เคอร์เนลอีกครั้งในระหว่างการทำงานของระบบในขณะที่เล่นด้วยหมุดรีเซ็ตและรีจิสเตอร์ต่าง ๆ บนสะพาน PCIe เช่นเดียวกับ PCI ดาวน์สตรีม เครื่อง และในการทำเช่นนั้นหลังจากรีบูตฉันอาจได้รับความตื่นตระหนกของเคอร์เนลหรือไดรเวอร์อื่น ๆ บ่นว่าพวกเขาได้รับการขัดจังหวะแม้ว่าจะไม่มีการดำเนินการเกิดขึ้น

ผู้จัดการตัดสินใจว่าควรจัดการกับอินเทอร์รัปต์อย่างไรจึงเป็นเรื่องลึกลับ

แก้ไข: ในกรณีที่เกี่ยวข้องกับสถาปัตยกรรม CPU ที่เป็นx86ปัญหา


1
stackoverflow.com/questions/14371513/for-a-shared-interrupt-line-how-do-i-find-which-interrupt-handler-to-usec
Ciro Santilli 新疆改造中心法轮功六四

คำตอบ:


14

ซึ่งจะกล่าวถึงในบทที่ 10ของไดรเวอร์อุปกรณ์ Linuxรุ่นที่ 3 โดย Corbet และคณะ มันสามารถใช้ได้ฟรีออนไลน์หรือคุณอาจจะโยนวิธี Shekels O'Reillyสำหรับต้นไม้ที่ตายแล้วหรือรูปแบบ ebook ส่วนที่เกี่ยวข้องกับคำถามของคุณเริ่มต้นที่หน้า 278 ในลิงค์แรก

สำหรับสิ่งที่คุ้มค่านี่คือความพยายามของฉันในการแปลความหมายทั้งสามหน้ารวมถึงบิตอื่น ๆ ที่ฉันได้ทำไป:

  • เมื่อคุณลงทะเบียนตัวจัดการ IRQ ที่ใช้ร่วมกันเคอร์เนลจะตรวจสอบว่า:

    ไม่มีตัวจัดการอื่นสำหรับขัดจังหวะนั้นหรือ

    ข ทุกคนที่ลงทะเบียนก่อนหน้านี้ยังร้องขอการแบ่งปันขัดจังหวะ

    หากใช้กรณีใดกรณีหนึ่งก็จะตรวจสอบว่าdev_idพารามิเตอร์ของคุณไม่ซ้ำกันหรือไม่เพื่อให้เคอร์เนลสามารถแยกแยะตัวจัดการหลายตัวได้เช่นระหว่างการลบตัวจัดการ

  • เมื่ออุปกรณ์ฮาร์ดแวร์PCI¹ยกสาย IRQ ระดับต่ำเคอร์เนลของขัดจังหวะจัดการที่เรียกว่าและในทางกลับกันเรียกร้องทั้งหมดของรถขนขัดจังหวะลงทะเบียนผ่านแต่ละหลังที่คุณใช้ในการลงทะเบียนผ่านทางจัดการdev_idrequest_irq()

    dev_idความต้องการความคุ้มค่าที่จะเป็นเครื่องที่ไม่ซ้ำกัน วิธีทั่วไปในการทำเช่นนั้นคือการส่งตัวชี้ไปยังอุปกรณ์แต่ละตัวที่structไดรเวอร์ของคุณใช้เพื่อจัดการอุปกรณ์นั้น เนื่องจากตัวชี้นี้จะต้องอยู่ในพื้นที่หน่วยความจำของไดรเวอร์ของคุณเพื่อให้เป็นประโยชน์กับไดรเวอร์จึงเป็นipso โดยเฉพาะสำหรับไดรเวอร์นั้น²

    หากมีไดรเวอร์หลายตัวที่ลงทะเบียนไว้สำหรับอินเทอร์รัปต์ที่กำหนดพวกเขาทั้งหมดจะถูกเรียกเมื่ออุปกรณ์ใด ๆยกสายอินเตอร์รัปต์นั้นขึ้นมา หากไม่ใช่อุปกรณ์ไดรเวอร์ของคุณที่ทำสิ่งนี้ตัวจัดการขัดจังหวะของไดรเวอร์จะถูกส่งผ่านdev_idค่าที่ไม่ได้เป็นของมัน ตัวจัดการขัดจังหวะของคนขับของคุณจะต้องกลับมาทันทีเมื่อเกิดเหตุการณ์นี้

    อีกกรณีหนึ่งคือไดรเวอร์ของคุณกำลังจัดการอุปกรณ์หลายอย่าง ตัวจัดการขัดจังหวะของคนขับจะได้รับหนึ่งในdev_idค่าที่คนขับรู้จัก รหัสของคุณควรสำรวจแต่ละอุปกรณ์เพื่อค้นหาว่ามีอุปกรณ์ใดขัดจังหวะ

    ตัวอย่าง Corbet และคณะ Give เป็นพอร์ตขนานของพีซี เมื่อมันทำการแทรกสัญญาณอินเตอร์รัปต์มันจะตั้งค่าบิตสูงสุดในการลงทะเบียนอุปกรณ์แรก (นั่นคือinb(0x378) & 0x80 == trueสมมติว่าหมายเลขพอร์ต I / O มาตรฐาน) เมื่อตัวจัดการของคุณตรวจพบสิ่งนี้ก็ควรที่จะทำงานแล้วล้าง IRQ ด้วยการเขียนค่าที่อ่านจากพอร์ต I / O กลับไปที่พอร์ตด้านบน ล้างบิตแล้ว

    ฉันไม่เห็นเหตุผลว่ากลไกพิเศษนั้นมีความพิเศษ อุปกรณ์ฮาร์ดแวร์ที่แตกต่างกันสามารถเลือกกลไกที่แตกต่างกัน สิ่งสำคัญเพียงอย่างเดียวคือการที่อุปกรณ์อนุญาตการขัดจังหวะที่ใช้ร่วมกันจะต้องมีวิธีให้ผู้ขับขี่อ่านสถานะการขัดจังหวะของอุปกรณ์และวิธีการล้างการขัดจังหวะ คุณจะต้องอ่านแผ่นข้อมูลหรือคู่มือการเขียนโปรแกรมของอุปกรณ์เพื่อค้นหากลไกที่อุปกรณ์ของคุณใช้

  • เมื่อตัวจัดการขัดจังหวะของคุณบอกเคอร์เนลมันจัดการการขัดจังหวะที่ไม่หยุดเคอร์เนลจากการดำเนินการต่อเพื่อเรียกตัวจัดการอื่น ๆ ที่ลงทะเบียนสำหรับการขัดจังหวะเดียวกันนั้น สิ่งนี้หลีกเลี่ยงไม่ได้หากคุณต้องการแชร์สายขัดจังหวะเมื่อใช้การขัดจังหวะระดับที่เรียก

    ลองนึกภาพอุปกรณ์สองชิ้นยืนยันการใช้อินเตอร์รัปต์สายเดียวกันในเวลาเดียวกัน (หรืออย่างน้อยก็ให้ปิดในเวลาที่เคอร์เนลไม่มีเวลาที่จะเรียกตัวจัดการขัดจังหวะเพื่อล้างบรรทัดและดังนั้นจึงเห็นการยืนยันที่สองแยกต่างหาก) เคอร์เนลจะต้องเรียกใช้ตัวจัดการทั้งหมดสำหรับบรรทัดขัดจังหวะนั้นเพื่อให้แต่ละ โอกาสในการสอบถามฮาร์ดแวร์ที่เกี่ยวข้องเพื่อดูว่าต้องการความสนใจหรือไม่ มีความเป็นไปได้ค่อนข้างมากที่โปรแกรมควบคุมสองตัวจะจัดการขัดจังหวะได้สำเร็จภายในการส่งผ่านเดียวกันผ่านรายการตัวจัดการสำหรับการขัดจังหวะที่กำหนด

    ด้วยเหตุนี้จึงจำเป็นที่ไดรเวอร์ของคุณจะบอกอุปกรณ์ว่ามีการจัดการเพื่อล้างการขัดจังหวะการยืนยันบางครั้งก่อนที่ตัวจัดการขัดจังหวะจะกลับมา ไม่ชัดเจนสำหรับฉันว่าจะเกิดอะไรขึ้น บรรทัดอินเตอร์รัปต์ที่ถูกยืนยันอย่างต่อเนื่องจะส่งผลให้เคอร์เนลเรียกใช้ตัวจัดการอินเตอร์รัพต์แบบแบ่งใช้อย่างต่อเนื่องหรือจะปกปิดความสามารถของเคอร์เนลเพื่อดูอินเทอร์รัปต์ใหม่ดังนั้นตัวจัดการจะไม่ถูกเรียก ไม่ว่าจะด้วยวิธีใดภัยพิบัติ


เชิงอรรถ:

  1. ฉันระบุ PCI ด้านบนเพราะทั้งหมดข้างต้นจะถือว่าอินเตอร์รัปต์ระดับที่ถูกเรียกใช้ซึ่งใช้ในสเปค PCI ดั้งเดิม ISA ใช้การขัดจังหวะแบบ edge-triggeredซึ่งทำให้การแบ่งปันนั้นยากที่สุดเท่าที่จะทำได้และเป็นไปได้แม้จะได้รับการสนับสนุนจากฮาร์ดแวร์เท่านั้น PCIe ใช้ข้อความส่งสัญญาณขัดจังหวะ; ข้อความขัดจังหวะมีค่าที่ไม่ซ้ำกันซึ่งเคอร์เนลสามารถใช้เพื่อหลีกเลี่ยงเกมเดารอบที่ต้องการด้วยการแบ่งปันอินเตอร์รัปต์ PCI PCIe อาจกำจัดความจำเป็นในการแบ่งปันการขัดจังหวะ (ฉันไม่รู้ว่ามันใช้งานได้จริงหรือไม่แค่ว่ามันมีศักยภาพ)

  2. เคอร์เนลไดรเวอร์ Linux ใช้พื้นที่หน่วยความจำร่วมกันทั้งหมด แต่ไดรเวอร์ที่ไม่เกี่ยวข้องไม่ควรจะล้อเล่นในพื้นที่หน่วยความจำของผู้อื่น ถ้าคุณไม่ผ่านตัวชี้ไปรอบ ๆ คุณสามารถมั่นใจได้ว่าไดรเวอร์อื่นจะไม่เกิดขึ้นกับค่าเดียวกันโดยไม่ได้ตั้งใจ


1
เช่นเดียวกับที่คุณกล่าวถึงตัวจัดการขัดจังหวะอาจถูกส่งผ่านdev_idที่ไม่ได้เป็นเจ้าของ สำหรับฉันดูเหมือนว่ามีโอกาสที่ไม่เป็นศูนย์ว่าไดรเวอร์ที่ไม่ได้เป็นเจ้าของdev_idโครงสร้างอาจยังคงเข้าใจผิดว่ามันเป็นของตัวเองตามวิธีที่ตีความเนื้อหา หากไม่ใช่กรณีนี้กลไกอะไรที่จะป้องกันสิ่งนี้
bsirang

คุณป้องกันไม่ให้มันทำdev_idบางสิ่งบางอย่างภายในพื้นที่หน่วยความจำของไดรเวอร์ของคุณ ไดรเวอร์อีกตัวอาจทำให้dev_idค่าที่เกิดขึ้นกับการสับสนกับตัวชี้ไปยังหน่วยความจำที่ไดรเวอร์ของคุณเป็นเจ้าของ แต่นั่นจะไม่เกิดขึ้นเพราะทุกคนเล่นตามกฎ นี่คือเคอร์เนลพื้นที่จำไว้ว่า: การมีวินัยในตนเองถือว่าเป็นเรื่องที่แตกต่างจากรหัสพื้นที่ผู้ใช้ซึ่งอาจสันนิษฐานได้ว่ามีสิ่งใดที่ไม่ได้รับอนุญาต
Warren Young

อ้างอิงจากบทที่สิบของ LDD3: "เมื่อใดก็ตามที่มีไดรเวอร์สองตัวหรือมากกว่านั้นใช้สายอินเตอร์รัปต์และฮาร์ดแวร์ขัดจังหวะตัวประมวลผลในบรรทัดนั้นเคอร์เนลจะเรียกใช้ตัวจัดการทุกตัวที่ลงทะเบียนสำหรับอินเตอร์รัปต์นั้น ไม่ถูกต้องเกี่ยวกับว่าตัวจัดการขัดจังหวะอาจถูกส่งผ่านในสิ่งdev_idที่มันไม่ได้เป็นเจ้าของ
bsirang

นั่นคือการอ่านผิดในส่วนของฉัน เมื่อฉันเขียนสิ่งนั้นฉันได้พูดถึงสองแนวคิด ฉันแก้ไขคำตอบของฉันแล้ว เงื่อนไขที่ต้องใช้ตัวจัดการขัดจังหวะของคุณเพื่อกลับอย่างรวดเร็วคือว่ามันถูกเรียกเนื่องจากการยืนยันขัดจังหวะโดยอุปกรณ์ที่ไม่ได้จัดการ ค่าของdev_idไม่ช่วยให้คุณตรวจสอบว่าสิ่งนี้เกิดขึ้น คุณต้องถามฮาร์ดแวร์ว่า "คุณดังขึ้นไหม?"
Warren Young

ใช่ตอนนี้ฉันต้องคิดออกว่าสิ่งที่ฉัน tinkering กับเป็นจริงทำให้เกิดไดรเวอร์อื่น ๆ ที่จะเชื่อว่าอุปกรณ์ของพวกเขา "รัง" kexecหลังจากที่เริ่มต้นของเคอร์เนลผ่าน
bsirang

4

เมื่อไดรเวอร์ร้องขอ IRQ ที่ใช้ร่วมกันจะส่งตัวชี้ไปยังเคอร์เนลเพื่ออ้างอิงถึงโครงสร้างเฉพาะอุปกรณ์ภายในพื้นที่หน่วยความจำของไดรเวอร์

อ้างอิงจาก LDD3:

เมื่อใดก็ตามที่มีไดรเวอร์อย่างน้อยสองตัวกำลังใช้สายอินเตอร์รัปต์และฮาร์ดแวร์ขัดจังหวะตัวประมวลผลบนบรรทัดนั้นเคอร์เนลจะเรียกใช้ตัวจัดการทุกตัวที่ลงทะเบียนสำหรับการอินเตอร์รัปต์นั้นผ่านแต่ละ dev_id ของตัวเอง

เมื่อตรวจสอบตัวจัดการ IRQ ของไดรเวอร์หลายตัวมันจะตรวจสอบฮาร์ดแวร์เองเพื่อตรวจสอบว่าควรจัดการกับอินเทอร์รัปต์หรือส่งคืนหรือIRQ_NONEไม่

ตัวอย่าง

ไดรเวอร์ UHCI-HCD
  status = inw(uhci->io_addr + USBSTS);
  if (!(status & ~USBSTS_HCH))  /* shared interrupt, not mine */
    return IRQ_NONE;

ในรหัสด้านบนไดรเวอร์จะอ่านการUSBSTSลงทะเบียนเพื่อตรวจสอบว่ามีการขัดจังหวะการบริการ

ไดร์เวอร์ SDHCI
  intmask = sdhci_readl(host, SDHCI_INT_STATUS);

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

เช่นเดียวกับในตัวอย่างก่อนหน้านี้ไดรเวอร์กำลังตรวจสอบการลงทะเบียนสถานะSDHCI_INT_STATUSเพื่อตรวจสอบว่ามันจำเป็นต้องให้บริการขัดจังหวะ

ไดรเวอร์ 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;

อีกตัวอย่างหนึ่ง


0

กรุณาเยี่ยมชมตรวจสอบลิงค์นี้:

เป็นวิธีปฏิบัติตามปกติเพื่อทริกเกอร์ครึ่งล่างหรือตรรกะอื่น ๆ ในตัวจัดการ IRQ หลังจากตรวจสอบสถานะ IRQ จากการลงทะเบียนหน่วยความจำที่แมป ดังนั้นปัญหาคือการแก้ไขเริ่มต้นโดยโปรแกรมเมอร์ที่ดี


เนื้อหาลิงก์ของคุณไม่พร้อมใช้งาน
user3405291
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.