精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Linux中斷虛擬化之二

云計算 虛擬化 Linux
計算機系統有很多的外設需要服務,顯然,CPU采用輪詢的方式逐個詢問外設是否需要服務,是非常浪費CPU的計算的,尤其是對那些并不是頻繁需要服務的設備。

[[437706]]

 PIC虛擬化

計算機系統有很多的外設需要服務,顯然,CPU采用輪詢的方式逐個詢問外設是否需要服務,是非常浪費CPU的計算的,尤其是對那些并不是頻繁需要服務的設備。因此,計算機科學家們設計了外設主動向CPU發起服務請求的方式,這種方式就是中斷。采用中斷方式后,在沒有外設請求時,CPU就可以繼續其他計算任務,而不是進行很多不必要的輪詢,極大地提高了系統的吞吐[1] 在每個指令周期結束后,如果CPU關中斷標識(IF)沒有被設置,那么其會去檢查是否有中斷請求,如果有中斷請求,則運行對應的中斷服務程序,然后返回被中斷的計算任務繼續執行。

CPU不可能為每個硬件都設計專門的管腳接收中斷,管腳數量的限制、電路的復雜度、靈活度等方方面面都不現實,因此,需要設計一個專門管理中斷的單元。由中斷管理單元接受來自外圍設備的請求,確定請求的優先級,并向CPU發出中斷。1981年IBM推出的第1代個人電腦PC/XT使用了一個獨立的8259A作為中斷控制器,自此,8259A就成為了單核時代中斷芯片事實上的標準。因為可以通過軟件編程對其進行控制,比如當管腳收到設備信號時,可以編程控制其發出的中斷向量號,因此,中斷控制器又稱為可編程中斷控制器(programmable interrupt controller),簡稱PIC。單片8259A可以連接8個外設的中斷信號線,可以多片級聯支持更多外設。

8259A和CPU的連接如圖5所示。

圖5 8259A和CPU連接

片選和地址譯碼器相連,當CPU準備訪問8259A前,需要向地址總線發送8259A對應的地址,經過譯碼器后,譯碼器發現是8259A對應的地址,因此會拉低與8259A的CS連接的管腳的電平,從而選中8259A,通知8259A,CPU準備與其交換數據了。

8259A的D0~7管腳與CPU的數據總線相連。從CPU向8259A發送ICW和OCW,從8259A向CPU傳送8259A的狀態以及中斷向量號,都是通過數據總線傳遞的。

當CPU向8259A發送ICW、OCW時,當把數據送上數據總線后,需要通知8259A讀數據,CPU通過拉低WR管腳的電平的方式通知8259A,當8259A的WR管腳收到低電平后,讀取數據總線的數據。類似的,CPU準備好讀取8259A的狀態時,拉低RD管腳通知8259A。

8259A和CPU之間的中斷信號的通知使用專用的連線,8259A的管腳INTR(interrupt request)和INTA(interrupt acknowledge)分別與處理器的INTR和INTA管腳相連。8259A通過管腳INTR向CPU發送中斷請求,CPU通過管腳INTA向PIC發送中斷確認,告訴PIC其收到中斷并且開始處理了。8259A與CPU之間的具體中斷過程如下:

1)8259A的IR0~7管腳高電平有效,所以當中斷源請求服務時,拉高連接IR0~7的管腳,產生中斷請求。

2)8259A需要將這些信號記錄下來,因此其內部有個寄存器IRR(Interrupt Request Register),負責記錄這個中斷請求,針對這個例子,IRR的bit 0將被設置為1。

3)有的時候,我們會屏蔽掉某個設備的中斷。換句話說,就是的當這個中斷源向8259A發送信號后,8259A并不將這個中斷信號發送給CPU。讀者不要將屏蔽和CPU通過cli命令關中斷混淆,CPU關中斷時,中斷還會發送給CPU,只是在關中斷期間CPU不處理中斷。8259A中的寄存器IMR(Interrupt Mask Register)負責記錄某個中斷源是否被屏蔽,比如0號中斷源被設備了屏蔽,那么IMR的bit 0將被設置。那么這個IMR是誰設置的呢?當然是CPU中的操作系統。因此這一步,8259A將會檢查收到的中斷請求是否被屏蔽。

4)在某一個時刻,可能有多個中斷請求,或者是之前存在IRR中的中斷并沒有被處理,8259A中積累了一些中斷。某一個時刻,8259A只能向CPU發送一個中斷請求,因此,當存在多個中斷請求時,8259A需要判斷一下中斷優先級,這個單元叫做priority resolver,priority resolver將在IRR中選出優先級最高的中斷。

5)選出最高優先級的中斷后,8259A拉高管腳INTR的電平,向CPU發出信號。

6)當CPU執行完當前指令周期后,其將檢查寄存器FLAGS的中斷使能位IF(Interrupt enable flag),如果允許中斷,那么將檢查INTR是否有中斷,如果有中斷,那么將通過管腳INTR通知8259A處理器將開始處理中斷。

7)8259A收到CPU發來的INTA信號后,將置位最高優先級的中斷在ISR(In-Service Register)中對應的位,并清空IRR中對應的位。

8)通常,x86 CPU會發送第2次INTA,在收到第2次INTA后,8259A會將中斷向量號(vector)送上數據總線D0~D7。

9)如果8259A設置為AEOI(Automatic End Of Interrupt)模式,那么8259A復位ISR中對應的bit,否則ISR中對應的bit一直保持到收到系統的中斷服務程序發來的EOI命令。

我們知道,中斷服務程序保存在一個數組中,數組中的每一項對應一個中斷服務程序。在實模式下,這個數組稱為IVT(interrupt vector table);在保護模式下,這個數組稱為IDT(Interrupt descriptor table)。

這個數組中保存的服務程序,并不是全部都是外部中斷,還有處理CPU內部異常的以及軟中斷服務程序。x86CPU前32個中斷號(0-31)留給處理器異常的,比如第0個中斷號,是處理器出現除0(Divide by Zero)異常的,不能被占用。因此,假設我們計劃IVT數組中第32個元素存放管腳IR0對應的ISR,那么我們初始化8259A時,通過ICW,設置起始的irq base為32,那么當8259A發出管腳IR0的中斷請求時,則發出的值是32,管腳IR1對應的是33,依此類推。這個32、33就是所謂的中斷向量(vector)。換句話說,中斷向量就是中斷服務程序在IVT/IDT中的索引。下面就是設置irq_base的代碼,在初始化時,通過第2個初始化命令字(ICW2)設置:

  1. commit 85f455f7ddbed403b34b4d54b1eaf0e14126a126 
  2. KVM: Add support for in-kernel PIC emulation 
  3. linux.git/drivers/kvm/i8259.c 
  4. static void pic_ioport_write(void *opaque, u32addr, u32 val) 
  5.   … 
  6.     switch(s->init_state) { 
  7.     … 
  8.     case 1: 
  9.       s->irq_base = val & 0xf8; 
  10.     … 
  11.     } 

后來,隨著APIC和MSI的出現,中斷向量設置的更為靈活,可以為每個PCI設置其中斷向量,這個中斷向量存儲在PCI設備的配置空間中。

內核中抽象了一個結構體kvm_kpic_state來記錄每個8259A的狀態:

  1. commit 85f455f7ddbed403b34b4d54b1eaf0e14126a126 
  2. KVM: Add support for in-kernel PIC emulation 
  3. struct kvm_kpic_state { 
  4.   u8last_irr;  /* edge detection */ 
  5.   u8 irr;  /* interrupt request register */ 
  6.   u8imr;   /* interrupt mask register */ 
  7.   u8isr;   /* interrupt service register */ 
  8.   … 
  9. }; 
  10.   
  11. struct kvm_pic { 
  12.   structkvm_kpic_state pics[2]; /* 0 is master pic, 1 is slave pic*/ 
  13.  irq_request_func *irq_request; 
  14.   void*irq_request_opaque; 
  15.   intoutput;   /* intr from master PIC */ 
  16.   structkvm_io_device dev; 
  17. }; 

1片8259A只能連接最多8個外設,如果需要支持更多外設,需要多片8259A級聯。在結構體kvm_pic中,我們看到有2片8259A:pic[0]和pic[1]。KVM定義了結構體kvm_kpic_state記錄8259A的狀態,其中包括我們之前提到的IRR、IMR、ISR等等。

1 虛擬設備向PIC發送中斷請求

如同物理外設請求中斷時拉高與8259A連接的管腳的電壓,虛擬設備請求中斷的方式是通過一個API告訴虛擬的8259A芯片中斷請求,以kvmtool中的virtio blk虛擬設備為例:

  1. commit 4155ba8cda055b7831489e4c4a412b073493115b 
  2. kvm: Fix virtio block device support some more 
  3. kvmtool.git/blk-virtio.c 
  4. static bool blk_virtio_out(…) 
  5.   … 
  6.   caseVIRTIO_PCI_QUEUE_NOTIFY: { 
  7.     … 
  8.     while(queue->vring.avail->idx != queue->last_avail_idx) { 
  9.       if(!blk_virtio_read(self, queue)) 
  10.        return false
  11.     } 
  12.    kvm__irq_line(self, VIRTIO_BLK_IRQ, 1); 
  13.   
  14.     break; 
  15.   } 
  16.   … 

當Guest內核的塊設備驅動發出I/O通知VIRTIO_PCI_QUEUE_NOTIFY時,將觸發CPU從Guest模式切換到Host模式,KVM中的塊模擬設備開始I/O操作,比如訪問保存Guest文件系統的鏡像文件。virtio blk這個提交,塊設備的I/O處理是同步的,也就是說,一直要等到文件操作完成,才會向Guest發送中斷,返回Guest。當然同步阻塞在這里是不合理的,而是應該馬上返回Guest,這樣Guest可以執行其他的任務,虛擬設備完成I/O操作后,再通知Guest,這是kvmtool初期的實現,后來已經改進為異步的方式。代碼中在一個while循環處理完設備驅動的I/O請求后,調用了函數kvm__irq_line,irq_line對應8259A的管腳IR0~7,其代碼如下:

  1. commit 4155ba8cda055b7831489e4c4a412b073493115b 
  2. kvm: Fix virtio block device support some more 
  3. kvmtool.git/kvm.c 
  4. void kvm__irq_line(struct kvm *self, int irq, intlevel) 
  5.   structkvm_irq_level irq_level; 
  6.   
  7.   irq_level= (struct kvm_irq_level) { 
  8.     { 
  9.      .irq    = irq, 
  10.     }, 
  11.    .level    = level
  12.   }; 
  13.   
  14.   if(ioctl(self->vm_fd, KVM_IRQ_LINE, &irq_level) < 0) 
  15.    die_perror("KVM_IRQ_LINE failed"); 
  16. 函數kvm__irq_line將irq number和管腳電平信息,這里是1,表示拉高電平了,封裝到結構體kvm_irq_level中,傳遞給內核中的KVM模塊: 
  17. commit 85f455f7ddbed403b34b4d54b1eaf0e14126a126 
  18. KVM: Add support for in-kernel PIC emulation 
  19. linux.git/drivers/kvm/kvm_main.c 
  20. static long kvm_vm_ioctl(…) 
  21.   … 
  22.   caseKVM_IRQ_LINE: { 
  23.     … 
  24.         kvm_pic_set_irq(pic_irqchip(kvm), 
  25.          irq_event.irq, 
  26.          irq_event.level); 
  27.     … 
  28.     break; 
  29.   } 
  30.   … 

KVM模塊將kvmtool中組織的中斷信息從用戶空間復制到內核空間中,然后調用虛擬8259A的模塊中提供的API kvm_pic_set_irq,向8259A發出中斷請求。

2 記錄中斷到IRR

中斷處理需要一個過程,從外設發出請求,一直到ISR處理完成發出EOI。而且可能中斷來了并不能馬上處理,或者之前已經累加了一些中斷,大家需要排隊依次請求CPU處理,等等,因此,需要一些寄存器來記錄這些狀態。當外設中斷請求到來時,8259A首先需要將他們記錄下來,這個寄存器就是IRR(Interrupt Request Register),8259A用他來記錄有哪些pending的中斷需要處理。

當KVM模塊收到外設的請求,調用虛擬8259A的API kvm_pic_set_irq是,其第1件事就是將中斷記錄到IRR寄存器中:

  1. commit 85f455f7ddbed403b34b4d54b1eaf0e14126a126 
  2. KVM: Add support for in-kernel PIC emulation 
  3. linux.git/drivers/kvm/i8259.c 
  4. void kvm_pic_set_irq(void *opaque, int irq, intlevel) 
  5.   structkvm_pic *s = opaque; 
  6.   
  7.  pic_set_irq1(&s->pics[irq >> 3], irq & 7, level); 
  8.   …… 
  9.   
  10. static inline void pic_set_irq1(structkvm_kpic_state *s, 
  11. int irq, int level
  12.   int mask; 
  13.   mask = 1<< irq; 
  14.   if(s->elcr & mask) /* level triggered */ 
  15.     … 
  16.   else  /* edge triggered */ 
  17.     if(level) { 
  18.       if((s->last_irr & mask) == 0) 
  19.        s->irr |= mask; 
  20.      s->last_irr |= mask; 
  21.     } else 
  22.      s->last_irr &= ~mask; 

信號有邊緣觸發和水平觸發,在物理上可以理解為,8329A在前一個周期檢測到管腳信號是0,當前周前檢測到管腳信號是1,如果是上升沿觸發模式,那么8259A就認為外設有請求了,這種觸發模式就是邊緣觸發。對于水平觸發,以高電平觸發為例,當8259A檢測到管腳處于高電平,則認為外設來請求了。

在虛擬8259A的結構體kvm_kpic_state中,寄存器elcr就是用來記錄8259A被設置的觸發模式的。參數level即相當于硬件層面的電信號,0表示低電平,1表示高電平。以邊緣觸發為例,當管腳收到一個低電平時,即level的值為0,代碼進入else分支,結構體kvm_kpic_state中的字段last_irr中會清除該IRQ對應IRR的位,即相當于設置該中斷管腳為低電平狀態。當管腳收到高電平時,即level的值為1,代碼進入if分支,此時8259A將判斷之前該管腳的狀態,也就是判斷結構體kvm_kpic_state中的字段last_irr中該IRQ對應IRR的位,如果為低電平,那么則認為中斷源有中斷請求,將其記錄到IRR中。當然,同時需要在字段last_irr記錄下當前該管腳的狀態。

3 設置中斷標識

當8259A將中斷請求記錄到IRR中后,下一步就是開啟一個中斷評估(evaluate)過程了,包括中斷是否被屏蔽,多個中斷請求的優先級等等,最后將通過管腳INTA通知CPU處理外部中斷。我們看到這里是8259A主動發起中斷過程,但是虛擬中斷有些不同,中斷的發起的時機不再是虛擬中斷芯片主動發起,而是在每次準備切入Guest時,KVM查詢中斷芯片,如果有pending的中斷,則執行中斷注入。模擬8259A在收到中斷請求后,在記錄到IRR后,設置一個變量,后面在切入Guest前KVM會查詢這個變量:

  1. commit 85f455f7ddbed403b34b4d54b1eaf0e14126a126 
  2. KVM: Add support for in-kernel PIC emulation 
  3. linux.git/drivers/kvm/i8259.c 
  4. void kvm_pic_set_irq(void *opaque, int irq, intlevel) 
  5.   structkvm_pic *s = opaque; 
  6.   
  7.  pic_set_irq1(&s->pics[irq >> 3], irq & 7, level); 
  8.  pic_update_irq(s); 
  9.   
  10. static void pic_update_irq(struct kvm_pic *s) 
  11.   … 
  12.   irq =pic_get_irq(&s->pics[0]); 
  13.   if (irq>= 0) 
  14.    s->irq_request(s->irq_request_opaque, 1); 
  15.   else 
  16.    s->irq_request(s->irq_request_opaque, 0); 
  17.   
  18. static void pic_irq_request(void *opaque, intlevel) 
  19.   struct kvm*kvm = opaque; 
  20.   
  21.  pic_irqchip(kvm)->output = level

在函數vmx_vcpu_run中,在準備切入Guest之前將調用函數vmx_intr_assist去檢查虛擬中斷芯片是否有等待處理的中斷,相關代碼如下:

  1. commit 85f455f7ddbed403b34b4d54b1eaf0e14126a126 
  2. KVM: Add support for in-kernel PIC emulation 
  3. linux.git/drivers/kvm/vmx.c 
  4. static int vmx_vcpu_run(…) 
  5.   … 
  6.    vmx_intr_assist(vcpu); 
  7.   … 
  8.   
  9. static void vmx_intr_assist(struct kvm_vcpu*vcpu) 
  10.   … 
  11.   has_ext_irq= kvm_cpu_has_interrupt(vcpu); 
  12.   … 
  13.   if(!has_ext_irq) 
  14.     return
  15.  interrupt_window_open = 
  16.    ((vmcs_readl(GUEST_RFLAGS) & X86_EFLAGS_IF) && 
  17.     (vmcs_read32(GUEST_INTERRUPTIBILITY_INFO) & 3) == 0); 
  18.   if(interrupt_window_open) 
  19.    vmx_inject_irq(vcpu, kvm_cpu_get_interrupt(vcpu)); 
  20.   … 

其中函數kvm_cpu_has_interrupt查詢8259A設置的變量output:

  1. commit 85f455f7ddbed403b34b4d54b1eaf0e14126a126 
  2. KVM: Add support for in-kernel PIC emulation 
  3. linux.git/drivers/kvm/irq.c 
  4. int kvm_cpu_has_interrupt(struct kvm_vcpu *v) 
  5.   structkvm_pic *s = pic_irqchip(v->kvm); 
  6.   
  7.   if(s->output)  /* PIC */ 
  8.     return1; 
  9.   return 0; 

如果有output設置了,就說明有外部中斷等待處理,然后接著判斷Guest是否可以被中斷,包括Guest是否中斷了,Guest是否正在執行一些不能被中斷的指令,如果可以注入,則調用vmx_inject_irq完成中斷的注入。其中,傳遞給函數vmx_inject_irq的第2個參數是函數kvm_cpu_get_interrupt返回的結果,該函數獲取需要注入的中斷,這個過程就是中斷評估過程,我們下一節討論。

4 中斷評估

在上一節我們看到在執行注入前,vmx_inject_irq調用函數kvm_cpu_get_interrupt獲取需要注入的中斷。函數kvm_cpu_get_interrupt的核心邏輯就是中斷評估(evaluate),包括:這個pending的中斷有沒有被屏蔽?pending的中斷的優先級是否比CPU正在處理的中斷優先級高?代碼如下:

  1. commit 85f455f7ddbed403b34b4d54b1eaf0e14126a126 
  2. KVM: Add support for in-kernel PIC emulation 
  3. linux.git/drivers/kvm/irq.c 
  4. int kvm_cpu_get_interrupt(struct kvm_vcpu *v) 
  5.   …… 
  6.   vector =kvm_pic_read_irq(s); 
  7.   if (vector!= -1) 
  8.     returnvector; 
  9.   … 
  10.   
  11. linux.git/drivers/kvm/i8259.c 
  12. int kvm_pic_read_irq(struct kvm_pic *s) 
  13.   int irq,irq2, intno; 
  14.   
  15.   irq =pic_get_irq(&s->pics[0]); 
  16.   if (irq>= 0) { 
  17.     … 
  18.       intno = s->pics[0].irq_base + irq; 
  19.   } else { 
  20.   … 
  21.   returnintno; 

kvm_pic_read_irq調用函數pic_get_irq獲取評估后的中斷,如上面黑體標識的,我們可以清楚的看到中斷向量和中斷管腳的關系,疊加了一個irq_base,這個irq_base就是通過初始化8259A時通過ICW設置的,完成從IRn到中斷向量的轉換。

一個中斷芯片通常連接有多個外設,所以在某一個時刻,可能會有多個設備請求到來,這時就有一個優先處理哪個請求的問題,因此,中斷就有了優先級的概念。以8259A為例,典型的有2種中斷優先級模式:

1)固定優先級(Fixedpriority),即優先級是固定的,從IR0到IR7依次降低,IR0的優先級永遠最高,IR7的優先級永遠最低。

2)循環優先級(rotatingpriority),即當前處理完的IRn其優先級調整為最低,當前處理的優先級下個,即IRn+1,調整為優先級最高。比如,當前處理的中斷是irq 2,那么緊接著irq3的優先級設置為是最高,然后依次是irq4、irq5、irq6、irq7、irq1、irq2、irq3。假設此時irq5和irq2同時來了中斷,那么irq5顯然會被優先處理。然后irq6被設置為優先級最高,接下來依次是irq7、irq1、irq2、irq3、irq4、irq5。

理解了循環優先級算法后,從8259A中獲取最高優先級請求的代碼就很容易理解了:

  1. commit85f455f7ddbed403b34b4d54b1eaf0e14126a126 
  2. KVM: Add support for in-kernel PIC emulation 
  3. linux.git/drivers/kvm/i8259.c 
  4. static int pic_get_irq(struct kvm_kpic_state *s) 
  5.   int mask,cur_priority, priority; 
  6.   
  7.   mask =s->irr & ~s->imr; 
  8.   priority =get_priority(s, mask); 
  9.   if(priority == 8) 
  10.     return-1; 
  11.   … 
  12.   mask =s->isr; 
  13.   … 
  14.  cur_priority = get_priority(s, mask); 
  15.   if(priority < cur_priority) 
  16.     /* 
  17.      *higher priority found: an irq should be generated 
  18.      */ 
  19.     return(priority + s->priority_add) & 7; 
  20.   else 
  21.     return-1; 
  22.   
  23. static inline int get_priority(structkvm_kpic_state *s, int mask) 
  24.   intpriority; 
  25.   if (mask== 0) 
  26.     return8; 
  27.   priority =0; 
  28.   while((mask & (1 << ((priority + s->priority_add) & 7))) == 0) 
  29.    priority++; 
  30.   returnpriority; 

函數pic_get_irq分成2部分,第1部分是從當前pending的中斷中取得最高優先級的中斷,當前之前需要濾掉被被屏蔽的中斷。第2部分是獲取正在被CPU處理的中斷的優先級的中斷的優先級,通過這里,讀者更能具體的理解了8259A為什么需要這些寄存器記錄中斷的狀態。然后比較2個中斷的優先級,如果pending的優先級高,那么就通過拉低INTR管腳電壓,向CPU發出中斷請求。

再來看一下計算優先級的函數get_priority。其中變量priority_add記錄的是當前最高優先級的管腳,所以邏輯上就是從當前最高的優先級管腳開始,從高向低依次檢查是否有pending的中斷。比如當前IR4最高,那么priority_add的值就是4。while循環,從管腳IR(4+0)開始,依次檢查管腳IR(4+1)、IR(4+2),依此類推。

5 中斷ACK

在物理上,CPU在準備處理一個中斷請求后,將通過管腳INTA向8259A發出確認脈沖。同樣,軟件模擬上,也需要類似處理。在完成中斷評估后,準備注入Guest前,需要向虛擬8259A執行確認狀態的操作,代碼如下:

  1. commit 85f455f7ddbed403b34b4d54b1eaf0e14126a126 
  2. KVM: Add support for in-kernel PIC emulation 
  3. linux.git/drivers/kvm/i8259.c 
  4. int kvm_pic_read_irq(struct kvm_pic *s) 
  5.   int irq,irq2, intno; 
  6.   
  7.   irq =pic_get_irq(&s->pics[0]); 
  8.   if (irq>= 0) { 
  9.    pic_intack(&s->pics[0], irq); 
  10.   … 
  11.   
  12. static inline void pic_intack(structkvm_kpic_state *s, int irq) 
  13.   if(s->auto_eoi) { 
  14.     … 
  15.   } else 
  16.    s->isr |= (1 << irq); 
  17.   /* 
  18.    * Wedon't clear a level sensitive interrupt here 
  19.    */ 
  20.   if(!(s->elcr & (1 << irq))) 
  21.    s->irr &= ~(1 << irq); 

在中斷評估中,在調用函數kvm_pic_read_irq獲取評估的中斷結果后,馬上就調用函數pic_intack完成了中斷確認的動作。在收到CPU發來的中斷確認后,8259A需要更新自己的狀態,包括,因為中斷已經開始得到服務了,所以從IRR中清除等待服務請求。另外,需要設置ISR位,記錄正在被服務的中斷,但是這里稍微有一點點復雜。

設置ISR表示正在服務的位,表示處理器正在處理中斷。設置ISR的一個典型作用是中斷的作用是當ISR處理完中斷,向8259A發送EOI時,8259A知道正在處理的IRn,比如說如果8259A使用的是循環優先級,那么需要最高優先級為當前處理的IRn的下一個。

如果8259A是AEOI模式,那么就無須設置ISR了,因為中斷服務程序執行完畢后不會發送EOI命令。所以在AEOI模式下(上面代碼的if分支),需要將收到EOI時8259A需要處理的邏輯完成,這部分內容我們在下一節會討論。

對于邊緣觸發的,進入到ISR階段后,需要復位對于IRR。對于level trigger的,在收到中斷請求后8259A處理,不過多討論了,如果讀者有興趣,可以閱讀函數pic_set_irq1中關于水平觸發部分的邏輯。

6 關于EOI的處理

中斷服務程序執行完成后,會向8259A發送EOI,告知8259A中斷處理完成。8259A在收到這個EOI時,復位ISR,如果是采用的循環優先級,還需要設置變量priority_add,使其指向當前處理IRn的下一個:

  1. commit85f455f7ddbed403b34b4d54b1eaf0e14126a126 
  2. KVM: Add support for in-kernel PIC emulation 
  3. linux.git/drivers/kvm/i8259.c 
  4. static void pic_ioport_write(void *opaque, u32addr, u32 val) 
  5.   … 
  6.       case1: /* end of interrupt */ 
  7.       case5: 
  8.        priority = get_priority(s, s->isr); 
  9.         if(priority != 8) { 
  10.          irq = (priority + s->priority_add) & 7; 
  11.          s->isr &= ~(1 << irq); 
  12.           if(cmd == 5) 
  13.            s->priority_add = (irq + 1) & 7; 
  14.          pic_update_irq(s->pics_state); 
  15.         } 
  16.        break; 
  17.   … 

如果8259A被設置為AEOI模式,不會再收到后續中斷服務程序的EOI命令,那么8259A在收到CPU的ACK后,就必須把收到EOI命令時執行的邏輯現在處理,前面看到AEOI模式不必設置ISR,所以這里也無需復位ISR,只需要調整變量priority_add,記錄最高優先級位置即可:

  1. commit 85f455f7ddbed403b34b4d54b1eaf0e14126a126 
  2. KVM: Add support for in-kernel PIC emulation 
  3. static inline void pic_intack(structkvm_kpic_state *s, int irq) 
  4.   if(s->auto_eoi) { 
  5.     if(s->rotate_on_auto_eoi) 
  6.      s->priority_add = (irq + 1) & 7;    
  7.   } else 
  8.   … 

7 中斷注入

對于外部中斷,每個CPU在指令周期結束后,將會去檢查INTR是否有中斷請求。那么對于處于Guest模式的CPU,其如何知道有中斷請求呢?Intel在VMCS中設置了一個字段:VM-entry interruption-information,在VM entry時CPU將會檢查這個字段,這個字段格式表3-1所示。

表3-1 VM-entry interruption-information格式(部分)

內容

7:0

中斷或異常向量

10:8

中斷類型:

0: External  interrupt

1: Reserved

2: Non-maskable  interrupt (NMI)

3: Hardware  exception

4: Software  interrupt

5: Privileged  software exception

6: Software  exception

7: Other event

31

是否有效[2] 

在VM entry前,KVM模塊檢查虛擬8259A中如果有pending中斷需要處理,則將需要處理的中斷信息寫入到VMCS中的這個字段VM-entry 

  1. interruption-information: 
  2. commit 85f455f7ddbed403b34b4d54b1eaf0e14126a126 
  3. KVM: Add support for in-kernel PIC emulation 
  4. linux.git/drivers/kvm/vmx.c 
  5. static void vmx_inject_irq(struct kvm_vcpu *vcpu,int irq) 
  6.   … 
  7.  vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, 
  8.       irq |INTR_TYPE_EXT_INTR | INTR_INFO_VALID_MASK); 

前面我們看到,中斷注入是在每次VM entry時,KVM模塊檢查8259A是否有pending的中斷等待處理。這樣就有可能給中斷帶來一定的延遲,典型如下面2類情況:

(1)CPU可能正處在Guest模式,那么就需要等待下一次VM exit 和VM entry。

(2)VCPU這個線程也許正在睡眠,比如Guest VCPU運行hlt指令時,就會切換回Host模式,線程掛起。

對于第1種情況,是多處理器系統下的一個典型情況,目標CPU的正在運行Guest。KVM需要想辦法觸發Guest發生一次VM exit,切換到Host。我們知道,當處于Guest模式的CPU收到外部中斷時,會觸發VM exit,由Host來處理這次中斷。所以,KVM可以向目標CPU發送一個IPI中斷,觸發目標CPU發生一次VM exit。

對于第2種情況,首先需要喚醒睡眠的VCPU線程,使其進入CPU就緒隊列,準備接受調度。對于多處理器系統,然后再向目標CPU發送一個“重新調度”的IPI中斷,那么被喚醒的VCPU線程很快就會被調度,執行切入Guest的過程,從而完成中斷注入。

所以當有中斷請求時,虛擬中斷芯片將主動“kick”一下目標CPU,這個“踢”的函數就是kvm_vcpu_kick:

  1. commit b6958ce44a11a9e9425d2b67a653b1ca2a27796f 
  2. KVM: Emulate hlt in the kernel 
  3. linux.git/drivers/kvm/i8259.c 
  4. static void pic_irq_request(void *opaque, intlevel) 
  5.   … 
  6.  pic_irqchip(kvm)->output = level
  7.   if (vcpu) 
  8.    kvm_vcpu_kick(vcpu); 

如果虛擬CPU線程在睡眠,則“踢醒”他。如果目標CPU運行在Guest模式,則將其從Guest模式“踢”到Host模式,在VM entry時完成中斷注入,kick的手段就是我們剛剛提到的IPI,代碼如下:

  1. commit b6958ce44a11a9e9425d2b67a653b1ca2a27796f 
  2. KVM: Emulate hlt in the kernel 
  3. linux.git/drivers/kvm/irq.c 
  4. void kvm_vcpu_kick(struct kvm_vcpu *vcpu) 
  5.   intipi_pcpu = vcpu->cpu; 
  6.   
  7.   if(waitqueue_active(&vcpu->wq)) { 
  8.    wake_up_interruptible(&vcpu->wq); 
  9.    ++vcpu->stat.halt_wakeup; 
  10.   } 
  11.   if(vcpu->guest_mode) 
  12.    smp_call_function_single(ipi_pcpu, vcpu_kick_intr, 
  13. vcpu, 0, 0); 

如果VCPU線程睡眠在等待隊列上,則喚醒使其進入CPU的就緒任務隊列。如果是多CPU的情況且目標CPU處于Guest模式,則需要發送核間中斷。如果目標CPU正在執行Guest,那么這個IPI中斷將導致VM exit,從而在下一次進入Guest時,可以注入中斷。

事實上,目標CPU無須執行任何callback,也無須等待IPI返回,所以也無須使用smp_call_function_single,而是直接發送一個請求目標CPU重新調度的IPI即可,因此后來直接調用了函數smp_send_reschedule。函數smp_send_reschedule簡單直接,直接發送了一個RESCHEDULE的IPI:

  1. commit 32f8840064d88cc3f6e85203aec7b6b57bebcb97 
  2. KVM: use smp_send_reschedule in kvm_vcpu_kick 
  3. linux.git/arch/x86/kvm/x86.c 
  4. void kvm_vcpu_kick(struct kvm_vcpu *vcpu) 
  5.   … 
  6.      smp_send_reschedule(cpu); 
  7.   … 
  8.   
  9. linux.git/arch/x86/kernel/smp.c 
  10. static void native_smp_send_reschedule(int cpu) 
  11.   … 
  12.  apic->send_IPI_mask(cpumask_of(cpu), RESCHEDULE_VECTOR); 

本文轉載自微信公眾號「Linux閱碼場」,可以通過以下二維碼關注。轉載本文請聯系Linux閱碼場公眾號。

 

責任編輯:武曉燕 來源: Linux閱碼場
相關推薦

2021-11-30 07:02:10

虛擬化Linux 中斷

2018-03-21 11:00:45

2020-11-23 07:19:15

Linux虛擬化KVM

2013-12-02 10:34:32

虛擬化實戰Cluster

2013-05-27 09:52:26

虛擬化存儲虛擬化應用

2010-05-26 14:42:54

桌面虛擬化

2023-09-19 10:00:34

Linux虛擬

2021-10-26 08:08:34

Node ExporLinux 監控

2009-12-25 10:05:00

Linux虛擬化Xen虛擬化

2009-10-21 12:49:25

Linux壓縮打包方法

2019-11-12 14:48:00

Linux桌面虛擬化KVM

2011-03-28 18:25:03

ibmdwLinux

2019-07-24 10:06:15

Linux網絡虛擬化

2019-05-13 16:37:35

Linux網絡虛擬化

2012-03-18 21:41:40

linux虛擬化

2017-07-01 07:02:41

虛擬化遷移

2021-12-10 08:45:45

Linux GIC Linux 系統

2020-01-02 10:34:32

Linux虛擬化Docker

2009-10-19 16:39:19

Linux虛擬化

2012-08-22 09:41:54

虛擬化
點贊
收藏

51CTO技術棧公眾號

欧美96在线丨欧| 成人性生活视频| 国产在线精品免费av| 欧美激情精品久久久久久蜜臀| 天天躁日日躁狠狠躁av| 人人草在线视频| 中文字幕免费不卡| 国产激情一区二区三区在线观看 | 婷婷在线免费观看| 日韩电影免费一区| 久久久久亚洲精品| 成人一级片免费看| 久久狠狠久久| 欧美日韩国产天堂| 国模无码视频一区二区三区| 欧美videos极品另类| 99精品视频一区| 成人午夜小视频| 日日噜噜噜噜人人爽亚洲精品| 天天做天天爱天天爽综合网| 亚洲欧洲国产伦综合| 中文字幕制服丝袜| 婷婷丁香久久| 日本精品视频一区二区| 青青在线免费观看| 黄色在线论坛| 欧美激情一区二区在线| 免费看国产精品一二区视频| 午夜老司机福利| 精品中文字幕一区二区小辣椒| 欧美亚洲一级片| 国产精品999久久久| 久久久久久久久久久妇女| 亚洲天堂网站在线观看视频| 国产999精品| 精品久久中出| 国产伦精品一区二区三区视频痴汉| 国产精品一卡| 国内精品久久久久久久久| 天海翼在线视频| 成人3d动漫在线观看| 日韩国产精品亚洲а∨天堂免| 日批视频在线看| 国产一区 二区| 精品视频999| 日本免费观看网站| 色老太综合网| 欧美三级欧美成人高清www| 日韩成人手机在线| 亚洲色图美国十次| 亚洲精品伦理在线| 国产免费一区二区三区四在线播放| 国产69精品久久app免费版| 久久在线免费观看| 欧美理论一区二区| 国产原创av在线| 久久香蕉国产线看观看99| 久久一区二区三区av| 深夜福利在线看| 91小视频在线观看| 欧美二区三区在线| 高清美女视频一区| 中文在线免费一区三区高中清不卡| 日韩久久精品一区二区三区| 国产精品二线| 亚洲欧洲日韩女同| 成人性做爰片免费视频| 四虎影视国产在线视频| 亚洲高清免费观看高清完整版在线观看| 青青视频免费在线观看| 搞黄网站在线看| 一本色道久久综合亚洲91| 日韩中文字幕组| 精品三级在线| 5月丁香婷婷综合| 亚洲成人精品在线播放| 久久久免费毛片| 亚洲色图18p| 乱老熟女一区二区三区| 国内久久视频| 日韩午夜免费| 日韩激情片免费| 亚洲AV无码成人精品区明星换面| 精品视频99| 美女久久久久久久| 久久久久久久黄色片| 青椒成人免费视频| 国产欧美一区二区三区不卡高清| 涩涩视频在线观看免费| 国产精品久久久一本精品 | 国产视频一区免费看| 国产福利成人在线| 精品国产va久久久久久久| av网站免费线看精品| 五月天色一区| 高清电影在线免费观看| 亚洲在线中文字幕| 男女视频一区二区三区| 午夜精品在线| 中文字幕不卡av| 日本三级视频在线| 蜜桃av噜噜一区| 国产视频99| 欧美性videos| 色欧美乱欧美15图片| 国产成人av免费观看| 国产欧美日韩精品一区二区免费| 美女视频久久黄| 中文字幕无线码一区| 91蜜桃在线免费视频| 9色视频在线观看| 嫩草伊人久久精品少妇av杨幂| 亚洲第一黄色网| 一区二区三区四区五区| 三级不卡在线观看| 国外成人在线视频网站| 毛片激情在线观看| 欧美性色综合网| 欧美 变态 另类 人妖| 在线精品国产| 国产精品偷伦一区二区| 视频福利在线| 亚洲成人综合在线| 红桃视频一区二区三区免费| 精品国产乱码| 欧美在线视频在线播放完整版免费观看| 一区二区日本伦理| 亚洲精品.www| 日韩一区欧美一区| 国产野外作爱视频播放| 最近国产精品视频| 18一19gay欧美视频网站| 亚洲国产精品久久久久久久| 国产精品麻豆一区二区| 国产福利一区视频| 国产麻豆精品久久| 国产成人精品久久久| 日本私人网站在线观看| 亚洲福利视频导航| 久久久久久久人妻无码中文字幕爆| 91精品国产乱码久久久久久 | 日韩日本欧美亚洲| 波多野结衣高清在线| 欧美极品影院| 亚洲午夜在线观看视频在线| 中文字幕精品一区二区三区在线| 精品国产成人| 国产精品夜间视频香蕉| 国产香蕉视频在线看| 色婷婷国产精品综合在线观看| 色噜噜在线观看| 性欧美xxxx大乳国产app| 国产综合第一页| 高清毛片在线观看| 日韩av中文字幕在线| 日本午夜视频在线观看| 国产喂奶挤奶一区二区三区| av五月天在线| 欧美电影一二区| 亚洲综合自拍一区| xxx.xxx欧美| 精品亚洲男同gayvideo网站| 欧美日韩乱国产| 亚洲国产精品黑人久久久| 中文字幕在线综合| 91精品国产乱码久久久久久久| 91在线观看免费观看| 日韩成人伦理| 亚洲精品成人久久久| 国产精品第5页| 国产精品免费av| 国产在线a视频| 国产精品毛片| 色吧亚洲视频| 日本一区影院| 欧美一区二区.| a√在线中文网新版址在线| 777奇米成人网| 久久久久久久久影院| 欧美激情一区二区在线| 无码国产精品一区二区高潮| 亚洲久色影视| 亚洲日本理论电影| 51精品国产| 国产成人91久久精品| 黄色动漫在线观看| 亚洲国产天堂久久综合网| 69av视频在线观看| 亚洲韩国一区二区三区| 国产熟女一区二区| 国产成人一区在线| 国产日韩成人内射视频| 亚洲精品2区| 欧美一区视久久| 久久伊人精品| 国产精品9999| 牛牛电影国产一区二区| 亚洲色图25p| 视频污在线观看| 欧美狂野另类xxxxoooo| 青草视频在线观看免费| 亚洲欧美视频在线观看视频| 野外性满足hd| 国产成人在线视频播放| 欧美一级裸体视频| 欧美日韩1080p| 亚洲精品国产精品国自产观看| av毛片精品| 91精品国产综合久久男男 | 国产日韩视频一区| 免费一级片91| 色综合av综合无码综合网站| 欧美国产三级| 亚洲综合欧美日韩| 国产精品免费99久久久| 国产女主播一区二区三区| 97久久精品一区二区三区的观看方式| 欧美又大粗又爽又黄大片视频| 4438x成人网全国最大| 中日韩美女免费视频网址在线观看| 丰满人妻一区二区三区无码av| 欧美日韩久久不卡| 国产精品视频一区在线观看| 亚洲国产一二三| 成年人av电影| 国产精品久久久久久久久免费丝袜 | 在线看免费毛片| 亚洲欧美久久| 91九色丨porny丨国产jk| 欧美.www| 神马午夜伦理影院| 免费看成人哺乳视频网站| 国产一区二区三区四区hd| 亚洲精品一二三**| 91免费综合在线| 亚洲电影二区| 成人黄色免费看| 欧美黄色成人| 国产情人节一区| 全球中文成人在线| 国产原创欧美精品| 欧美亚洲黄色| 国产日韩在线视频| 中文字幕日韩亚洲| 91中文字幕在线观看| 精品视频在线观看免费观看| 91精品国产自产在线老师啪| 亚洲国产91视频| 亚洲曰本av电影| 香蕉大人久久国产成人av| 99理论电影网| 久久精品色综合| 欧美高清性xxxxhd| 精品国产一区二区三区| 亚洲二区自拍| 亚洲精彩视频| 福利视频免费在线观看| 亚洲精品护士| 国产成人亚洲精品无码h在线| 蜜桃视频一区| 天天干天天玩天天操| 国产一区二区三区视频在线播放| 亚洲三级在线视频| 成人国产一区二区三区精品| 中文成人无字幕乱码精品区| 久久毛片高清国产| 激情高潮到大叫狂喷水| 一区二区三区不卡在线观看| 国产无码精品视频| 色噜噜狠狠色综合中国| 91精东传媒理伦片在线观看| 欧美一级二级三级乱码| 少妇av在线播放| 国产午夜精品视频| 成人在线观看免费网站| 国模极品一区二区三区| 91看片一区| 97se视频在线观看| 亚洲国产精品嫩草影院久久av| 日韩欧美在线一区二区| 一级毛片免费高清中文字幕久久网| 国产成人一区二区三区别| 美女爽到呻吟久久久久| 国产免费中文字幕| 99精品久久99久久久久| 91导航在线观看| 日韩黄色一级片| 日韩一区视频在线| 浮生影视网在线观看免费| 久久亚洲私人国产精品va | 国产v综合ⅴ日韩v欧美大片| 色婷婷成人网| 久久99热只有频精品91密拍| 99久精品视频在线观看视频| 成年人网站国产| 蜜臀av一级做a爰片久久| 无码av免费精品一区二区三区| 国产欧美综合在线| 亚洲国产精一区二区三区性色| 欧美综合一区二区| 色一情一乱一乱一区91av| 色婷婷综合成人av| 日韩伦理在线一区| 亚洲综合精品一区二区| 美日韩中文字幕| 成人av在线播放观看| 日韩国产欧美一区二区三区| 亚洲一区和二区| 中文字幕一区二区三区四区不卡 | 欧洲高清一区二区| 黄色亚洲在线| 天堂在线一区二区三区| 久久老女人爱爱| 精品亚洲永久免费| 7777精品伊人久久久大香线蕉的 | 全色精品综合影院| 欧美国产日韩一区二区在线观看 | 国产在视频线精品视频| 欧美日韩裸体免费视频| 国产高清在线免费| 久久九九有精品国产23| 欧美色片在线观看| 欧美激情第六页| 香蕉久久国产| 7788色淫网站小说| 亚洲第一成年网| 欧美自拍偷拍第一页| 欧美插天视频在线播放| 成人动漫视频在线观看| 亚洲午夜在线观看| 理论电影国产精品| 精品熟妇无码av免费久久| 色婷婷激情综合| 欧美女优在线| 奇米4444一区二区三区 | 亚洲欧美www| 三级中文字幕在线观看| 欧美亚洲国产一区二区三区va | 日韩精品免费一区二区夜夜嗨 | 四虎影视4hu4虎成人| 久久精品成人一区二区三区蜜臀| 亚洲欧洲日本mm| 男人网站在线观看| 亚洲成av人片| 天天干天天爱天天操| 88xx成人精品| 日本欧美高清| 免费午夜视频在线观看| 久久久久久久免费视频了| 日日骚av一区二区| 亚洲香蕉成视频在线观看| 人人鲁人人莫人人爱精品| 日本不卡在线播放| 欧美bbbbb| 国产色无码精品视频国产| 日韩一级精品视频在线观看| gogo在线观看| 国产一区二区三区四区hd| 欧美资源在线| 男女男精品视频网站| 欧美一区二区三区色| 亚洲色图美国十次| 精品国产一二| 日韩国产一区二| 老熟妻内射精品一区| 欧美v日韩v国产v| 无遮挡在线观看| 婷婷久久青草热一区二区| 激情综合网av| 国产大片中文字幕| 国产亚洲精品高潮| 亚洲福利影视| 日韩av新片网| 国产亚洲综合av| 国产精品一区二区人人爽| 欧美国产日韩在线| 综合亚洲自拍| 天天久久综合网| 五月激情六月综合| av在线1区2区| 超碰97国产在线| 天堂久久一区二区三区| 国产天堂av在线| 日韩成人xxxx| 亚洲资源在线| 极品美女扒开粉嫩小泬| 国产精品污污网站在线观看| 性做久久久久久久久久| 国产91九色视频| 午夜精品剧场| 国产精品美女高潮无套| 日韩一级片在线播放| 欧美黑人一区| r级无码视频在线观看| 欧美国产日韩一二三区| 国产 日韩 欧美 精品| 国产精品入口日韩视频大尺度| 伊人久久综合| 亚洲一二三四五六区| 亚洲精品一区二区三区婷婷月|