iT邦幫忙

0

[6.1810][code] xv6 的 Device (三) : virtio_disk (二)

  • 分享至 

  • xImage
  •  

系列文章 : [6.1810] 跟著 MIT 6.1810 學習基礎作業系統觀念

大綱

  • kernel/virtio_disk.c/virtio_disk_init
  • kernel/virtio_disk.c/alloc_desc
  • kernel/virtio_disk.c/free_desc
  • kernel/virtio_disk.c/free_chain
  • kernel/virtio_disk.c/alloc3_desc
  • kernel/virtio_disk.c/virtio_disk_rw
  • kernel/virtio_disk.c/virtio_disk_intr

kernel/virtio_disk.c/virtio_disk_init

這個 function 會對 virtio disk 進行初始化,所以我們可以先看看 spec 上怎麼定義初始化的順序。

{ 4.2.3.1.1 Driver Requirements: Device Initialization }

  • 讀取 MagicValue 以及 Version register,並確認值符合預期
  • 讀取 DeviceID register
    • 假如 Device ID 是 0 的話 ( 根據 VirtIO spec 5 Device Types, 0 代表 invalid device ),就必須立刻停止初始化,也不可以存取其他的 register。

{ 3.1.1 Driver Requirements: Device Initialization }

驅動程式需要依照下列的順序來初始化一個 VirtIO device

  1. Reset the device
  • Status register 寫入 0,可以 reset 這個 device
  1. Set the ACKNOWLEDGE status bit: the guest OS has noticed the device.
  • ACKNOWLEDGE status bit 是 第一個 bit
  • { 2.1 Device Status Field }
  1. Set the DRIVER status bit: the guest OS knows how to drive the device.
  • { 2.1 Device Status Field }
  1. 閱讀 DeviceFeatures register ( device feature bits ),並且寫入 Guest OS 可以支援的,該群 bits 的 subset。在過程中,guest OS 的 driver 可能會去讀取 ( 但絕不能寫入 ) device-specific configuration ( Configuration space )。
  • memory-mapped register layout 在 { 4.2.2 MMIO Device Register Layout }
  1. Set the FEATURES_OK status bit. The driver MUST NOT accept new feature bits after this step.
  2. Re-read device status to ensure the FEATURES_OK bit is still set: otherwise, the device does not support our subset of features and the device is unusable.
  3. Perform device-specific setup, including discovery of virtqueues for the device, optional per-bus setup, reading and possibly writing the device’s virtio configuration space, and population of virtqueues.
  • device-specific setup : VirtIO device 可以區分成 network card, block device, console … 等等
  1. Set the DRIVER_OK status bit. At this point the device is “live”.

假如上述的步驟有錯的話,guest OS driver 必須要設定 FAILED status bits { 2.1 Device Status Field },表示 guest OS 已經放棄這個 VirtIO device 了, driver 不可以繼續初始化這個 device。


{ 4.2.3.2 Virtqueue Configuration }

The driver will typically initialize the virtual queue in the following way:

  1. 把 index ( first queue is 0 ) 寫進 QueueSel register,藉此來選擇想要設定的 queue。
  2. Check if the queue is not already in use: read QueueReady, and expect a returned value of zero (0x0).
  3. Read maximum queue size (number of elements) from QueueNumMax. If the returned value is zero (0x0) the queue is not available.
  4. Allocate and zero the queue pages, making sure the memory is physically contiguous. It is recommended to align the Used Ring to an optimal boundary (usually the page size).
  5. Notify the device about the queue size by writing the size to QueueNum.
  6. Write physical addresses of the queue’s Descriptor Table, Available Ring and Used Ring to (respectively) the QueueDescLow/QueueDescHigh, QueueAvailLow/QueueAvailHigh and QueueUsedLow/QueueUsedHigh register pairs.
  7. Write 0x1 to QueueReady.

對於 VirtIO device 的 memory-mapped register 的模擬,可以參考 QEMU 的程式碼。對照 VirtIO device 的 spec 以及 QEMU 的程式碼,可能也是個有趣的學習經驗。
https://github.com/qemu/qemu/blob/stable-10.0/hw/virtio/virtio-mmio.c


void
virtio_disk_init(void)
{
  uint32 status = 0;

這個變數的意義是 VirtIO 裡的 Device Status。
( VirtIO spec : 2.1 Device Status Field )


  initlock(&disk.vdisk_lock, "virtio_disk");

確保每次只有一個 CPU 可以控制,送 command 給這個 VirtIO blk device。


  if(*R(VIRTIO_MMIO_MAGIC_VALUE) != 0x74726976 ||
     *R(VIRTIO_MMIO_VERSION) != 2 ||
     *R(VIRTIO_MMIO_DEVICE_ID) != 2 ||
     *R(VIRTIO_MMIO_VENDOR_ID) != 0x554d4551){
    panic("could not find virtio disk");
  }

這邊會去讀取一些 MMIO ( memory-mapped I/O address ),跟這個裝置溝通,並確定我們是在跟正確的 device 對話。

  • MAGIC_VALUE: 必須是 0x74726976
    • ASCII 的 virt
  • VERSION: 2
    • 標準的 VirtIO 版本,也是 QEMU 實作的版本。
    • 需要注意的是,gem5 這個模擬器使用的是 version 1 ,也就是舊版的 VirtIO
  • DEVICE_ID : 2
    • 因為我們希望這個 VirtIO device 是 block device ( a hard disk ),所以會是 2。
  • VENDOR_ID : 0x554d4551
    • ASCII 為 “QEMU”
    • 因為目前是在 QEMU 上執行,所以會是這個值。假如將來想要移植到其他平台 ( e.g. gem5 ),就會需要修改這個值。

  // reset device
  *R(VIRTIO_MMIO_STATUS) = status;

Device Initialization 步驟 1 : Reset the device


  // set ACKNOWLEDGE status bit
  status |= VIRTIO_CONFIG_S_ACKNOWLEDGE;
  *R(VIRTIO_MMIO_STATUS) = status;

Device Initialization 步驟 2 : Set the ACKNOWLEDGE status bit: the guest OS has noticed the device.


  // set DRIVER status bit
  status |= VIRTIO_CONFIG_S_DRIVER;
  *R(VIRTIO_MMIO_STATUS) = status;

Device Initialization 步驟 3 : Set the DRIVER status bit: the guest OS knows how to drive the device.


  // negotiate features
  uint64 features = *R(VIRTIO_MMIO_DEVICE_FEATURES);

Device Initialization 步驟 4 : 閱讀 DeviceFeatures register ( device feature bits )


  features &= ~(1 << VIRTIO_BLK_F_RO);
  features &= ~(1 << VIRTIO_BLK_F_SCSI);
  features &= ~(1 << VIRTIO_BLK_F_CONFIG_WCE);
  features &= ~(1 << VIRTIO_BLK_F_MQ);
  features &= ~(1 << VIRTIO_F_ANY_LAYOUT);
  features &= ~(1 << VIRTIO_RING_F_EVENT_IDX);
  features &= ~(1 << VIRTIO_RING_F_INDIRECT_DESC);

這邊會把 xv6-riscv 不支援的 features 關掉。

  • VIRTIO_BLK_F_RO
  • VIRTIO_BLK_F_SCSI
  • VIRTIO_BLK_F_CONFIG_WCE
  • VIRTIO_BLK_F_MQ
  • VIRTIO_F_ANY_LAYOUT
  • VIRTIO_RING_F_EVENT_IDX
  • VIRTIO_RING_F_INDIRECT_DESC

  *R(VIRTIO_MMIO_DRIVER_FEATURES) = features;

Device Initialization 步驟 4 : 寫入 Guest OS 可以支援的,該群 bits 的 subset。


  // tell device that feature negotiation is complete.
 status |= VIRTIO_CONFIG_S_FEATURES_OK;
  *R(VIRTIO_MMIO_STATUS) = status;

Device Initialization 步驟 5 : Set the FEATURES_OK status bit. The driver MUST NOT accept new feature bits after this step.


  // re-read status to ensure FEATURES_OK is set.
  status = *R(VIRTIO_MMIO_STATUS);
  if(!(status & VIRTIO_CONFIG_S_FEATURES_OK))
    panic("virtio disk FEATURES_OK unset");

Device Initialization 步驟 6 : Re-read device status to ensure the FEATURES_OK bit is still set: otherwise, the device does not support our subset of features and the device is unusable.


  // initialize queue 0.
  *R(VIRTIO_MMIO_QUEUE_SEL) = 0;

Device Initialization 步驟 7.
Virtqueue Configuration 步驟 1 : Select the queue writing its index (first queue is 0) to QueueSel.

一個 VirtIO device 可能會有多個 queue。而這個 register 就是來讓我們可以選擇目前我們想設定的是哪一個 queue。

在這裡,我們想設定的是第 0 個 queue ( queue 0 )


  // ensure queue 0 is not in use.
  if(*R(VIRTIO_MMIO_QUEUE_READY))
    panic("virtio disk should not be ready");

Device Initialization 步驟 7.
Virtqueue Configuration 步驟 2 : Check if the queue is not already in use: read QueueReady, and expect a returned value of zero (0x0)

因為我們剛 reset 這個 device,所以 ready bit 必須要是 0。


  // check maximum queue size.
  uint32 max = *R(VIRTIO_MMIO_QUEUE_NUM_MAX);
  if(max == 0)
    panic("virtio disk has no queue 0");
  if(max < NUM)
    panic("virtio disk max queue too short");

Device Initialization 步驟 7.
Virtqueue Configuration 步驟 3 : Read maximum queue size (number of elements) from QueueNumMax. If the returned value is zero (0x0) the queue is not available.

  • 假如 register VIRTIO_MMIO_QUEUE_NUM_MAX 讀取出來的值是 0,表示該 queue 不存在,需要發 panic
  • NUM : xv6-riscv 預期的 queue size,預設是 8。
  • 假如該 VirtIO device 可以支援的 queue size 比 xv6-riscv 預期的還要少,就要發一個 panic。

  // allocate and zero queue memory.
  disk.desc = kalloc();
  disk.avail = kalloc();
  disk.used = kalloc();
  if(!disk.desc || !disk.avail || !disk.used)
    panic("virtio disk kalloc");
  memset(disk.desc, 0, PGSIZE);
  memset(disk.avail, 0, PGSIZE);
  memset(disk.used, 0, PGSIZE);

Device Initialization 步驟 7.
Virtqueue Configuration 步驟 4 : Allocate and zero the queue pages, making sure the memory is physically contiguous. It is recommended to align the Used Ring to an optimal boundary (usually the page size).

把一些跟 Guest OS 以及 VirtIO device 共享的資料歸 0,每一個 virtqueue 包含以下三個 data structure。

  • desc ( virtq_desc )
  • avail ( virtq_avail )
  • used ( virtq_used )

  // set queue size.
  *R(VIRTIO_MMIO_QUEUE_NUM) = NUM;

Device Initialization 步驟 7.
Virtqueue Configuration 步驟 5 : Notify the device about the queue size by writing the size to QueueNum.

告訴 VirtIO device,xv6-riscv ( guest OS ) 會使用 virtqueue 裡面的 NUM 個 entry。


  // write physical addresses.
  *R(VIRTIO_MMIO_QUEUE_DESC_LOW) = (uint64)disk.desc;
  *R(VIRTIO_MMIO_QUEUE_DESC_HIGH) = (uint64)disk.desc >> 32;
  *R(VIRTIO_MMIO_DRIVER_DESC_LOW) = (uint64)disk.avail;
 *R(VIRTIO_MMIO_DRIVER_DESC_HIGH) = (uint64)disk.avail >> 32;
  *R(VIRTIO_MMIO_DEVICE_DESC_LOW) = (uint64)disk.used;
  *R(VIRTIO_MMIO_DEVICE_DESC_HIGH) = (uint64)disk.used >> 32;

Device Initialization 步驟 7.
Virtqueue Configuration 步驟 6 : Write physical addresses of the queue’s Descriptor Table, Available Ring and Used Ring to (respectively) the QueueDescLow/QueueDescHigh, QueueAvailLow/QueueAvailHigh and QueueUsedLow/QueueUsedHigh register pairs.

  • QUEUE_DESC: The descriptor area.
  • DRIVER_DESC: The available ring.
  • DEVICE_DESC: The used ring

  // queue is ready.
  *R(VIRTIO_MMIO_QUEUE_READY) = 0x1;

Device Initialization 步驟 7.
Virtqueue Configuration 步驟 7 : Write 0x1 to QueueReady.

寫 1 給 virtqueue 0 的 QUEUE_READY 是在告訴 VirtIO device, queue 0 已經設定好了,並且 ready to use


  // all NUM descriptors start out unused.
  for(int i = 0; i < NUM; i++)
    disk.free[i] = 1;

這部分跟 VirtIO device 硬體本身無關,是 guest OS 用自己的方法去追蹤哪一個 descriptor 是閒置的。


  // tell the device we're completely ready.
  status |= VIRTIO_CONFIG_S_DRIVER_OK;
  *R(VIRTIO_MMIO_STATUS) = status;

  // plic.c and trap.c arrange for interrupts from VIRTIO0_IRQ.
}

Device Initialization 步驟 8 : Set the DRIVER_OK status bit. At this point the device is “live”.
這表示 guest OS 的對 VirtIO device 的 driver 已經初始化完畢了,現在 VirtIO block device 可以開始接受 read/write requests 了。



kernel/virtio_disk.c/alloc_desc

// find a free descriptor, mark it non-free, return its index.
static int
alloc_desc()
{
  for(int i = 0; i < NUM; i++){
    if(disk.free[i]){
      disk.free[i] = 0;
      return i;
    }
  }
  return -1;
}

找尋有沒有空閒的 disk.desc

  • 沒有的話, return -1
  • 有的話,return disk.desc 陣列的 index


kernel/virtio_disk.c/free_desc

// mark a descriptor as free.
static void
free_desc(int i)
{
  if(i >= NUM)
    panic("free_desc 1");
  if(disk.free[i])
    panic("free_desc 2");
  disk.desc[i].addr = 0;
  disk.desc[i].len = 0;
  disk.desc[i].flags = 0;
  disk.desc[i].next = 0;
  disk.free[i] = 1;

釋放一個 descriptor,供後續的 request 使用。


 wakeup(&disk.free[0]);

假如有人想要 alloc descriptor 失敗的話,就會陷入睡眠。
這邊會把陷入睡眠的 process 叫醒。



kernel/virtio_disk.c/free_chain

https://github.com/TommyWu-fdgkhdkgh/xv6-riscv/blob/xv6-riscv-rev5/kernel/virtio_disk.c#L186

從 desciprtor chain 的頭開始 free 掉一整個 descriptor chain。



kernel/virtio_disk.c/alloc3_desc

alloc3_desc(int *idx)

因為 xv6-riscv 的設計是每次對 VirtIO block disk 的操作都需要三個 descriptor,所以這個 function 就是一次 alloc 三個 descriptor,並把取得的 index 放進 idx 陣列。


   if(idx[i] < 0){
      for(int j = 0; j < i; j++)
        free_desc(idx[j]);
      return -1;
    }

這邊有個小巧思,就是當我們 alloc 失敗的時候,要把之前 alloc 成功的 descriptor 還回去。



kernel/virtio_disk.c/virtio_disk_rw

void
virtio_disk_rw(struct buf *b, int write)
{
  • buf->blockno : 我們想要處理的 block number,因為 xv6-riscv 的 BSIZE 是 1024,所以每一個 block 代表了 1024 bytes
  • buf->data : 這個 block 的 1024 bytes 的資料。
  • write != 0 : 代表要寫入 disk
  • write == 0 : 代表要從 disk 讀取資料

  uint64 sector = b->blockno * (BSIZE / 512);

把 block number 轉換成 sector number。

  • 每一個 block 是 1024 bytes
  • 每一個 sector 是 512 bytes
  • 每一個 xv6-riscv 的 block 是 2 個 sector

  acquire(&disk.vdisk_lock);

代表每次只有一個 CPU 可以進行 VirtIO block device 的操作。


  // the spec's Section 5.2 says that legacy block operations use
  // three descriptors: one for type/reserved/sector, one for the
  // data, one for a 1-byte status result.

  // allocate the three descriptors.
  int idx[3];
  while(1){
    if(alloc3_desc(idx) == 0) {
      break;
    }
    sleep(&disk.free[0], &disk.vdisk_lock);
  }

嘗試 alloc 三個 descriptor,失敗的話就去睡覺。
當我們在 free descriptor 的時候,會被叫醒。


  // format the three descriptors.
  // qemu's virtio-blk.c reads them.

  struct virtio_blk_req *buf0 = &disk.ops[idx[0]];

每次對 VirtIO block device 請求都會需要有一個 virtio_blk_req。
會將 virtio_blk_req 放在第一個 descriptor。

要注意的是,在 VirtIO spec 裡面,virtio_blk_req 還包含了 data 以及 status,但在 xv6-riscv 裡面,virtio_blk_req 沒有那兩樣東西。

xv6-riscv 會把 data 放在第二個 descriptor,並把 status 放在第三個 descriptor。


  if(write)
    buf0->type = VIRTIO_BLK_T_OUT; // write the disk
  else
    buf0->type = VIRTIO_BLK_T_IN; // read the disk
  buf0->reserved = 0;
  buf0->sector = sector;

把相對應的資訊放進 virtio_blk_req


  disk.desc[idx[0]].addr = (uint64) buf0;
  disk.desc[idx[0]].len = sizeof(struct virtio_blk_req);
  disk.desc[idx[0]].flags = VRING_DESC_F_NEXT;
  disk.desc[idx[0]].next = idx[1];

第一個 descriptor 會放 xv6-riscv 的 virtio_blk_req。

  • addr : virtio_blk_req 在 xv6-riscv 內的 physical address
  • len : 該資料的長度
  • flags = VRING_DESC_F_NEXT : 代表這個 descriptor 跟下一個 descriptor 會串接在一起
  • next : 下一個 descriptor 在 disk.desc 陣列內的 index。

  disk.desc[idx[1]].addr = (uint64) b->data;
  disk.desc[idx[1]].len = BSIZE;
  if(write)
    disk.desc[idx[1]].flags = 0; // device reads b->data
  else
    disk.desc[idx[1]].flags = VRING_DESC_F_WRITE; // device writes b->data
  disk.desc[idx[1]].flags |= VRING_DESC_F_NEXT;
  disk.desc[idx[1]].next = idx[2];

第二個 descriptor 會放 data

  • addr : data 的 physical address
    • 假如現在是想要從 disk 讀取資料,則讀出來的資料會放在這裡
    • 假如現在是想要把資料寫進 disk,則 VirtIO Block Device 會從這個 address 抓取資料
  • len : 該資料的長度
  • flags
    • VRING_DESC_F_WRITE : 代表 VirtIO device 會對 b->data 進行寫入,以 driver 的角度而言,是從 disk 讀取資料。
    • VRING_DESC_F_NEXT : 代表這個 descriptor 跟下一個 descriptor 會串接在一起
  • next : 下一個 descriptor 在 disk.desc 陣列內的 index。

  disk.info[idx[0]].status = 0xff; // device writes 0 on success
  disk.desc[idx[2]].addr = (uint64) &disk.info[idx[0]].status;
  disk.desc[idx[2]].len = 1;
  disk.desc[idx[2]].flags = VRING_DESC_F_WRITE; // device writes the status
  disk.desc[idx[2]].next = 0;

第三個 descriptor 會放 status

  • status 可以讓 VirtIO Block Device 回報訊息,當 VirtIO block device 寫 0,代表這次的請求有成功。
  • addr : status 的 physical address
  • len : 該資料的長度為 1 byte。
  • flag
    • VRING_DESC_F_WRITE : 代表 VirtIO device 會對該位址進行寫入

  // record struct buf for virtio_disk_intr().
  b->disk = 1;
  disk.info[idx[0]].b = b;
  • b->disk = 1 代表 VirtIO Block device 還在處理這個請求。
  • disk.info[idx[0]].b = b
    • idx[0] 代表 disk.desc 陣列內的 index,也就是 chain of descriptors 的第一個 descriptor 的 index。
    • 在這邊把 struct buf *b 記錄下來,讓我們可以在將來的 interrupt handler 中使用這個指標,並利用這個指標,把相對應的 process 喚醒 ( wake up )。

  // tell the device the first index in our chain of descriptors.
  disk.avail->ring[disk.avail->idx % NUM] = idx[0];

把 chain of descriptors 裡的第一個 descriptor 在 disk.desc 陣列裡的 index,放進 disk.avail 裡面,以此來通知 VirtIO block device,chain of descriptors 第一個 descriptor 是哪一個。


  __sync_synchronize();

這個 memory barrier 可以防止 compiler 或是 CPU 把這些記憶體的寫入重新排序。它保證了對 disk.avail 的寫入,可以在 index 更新 ( 下一行 ) 之前發生,確保我們是寫入正確的 index。


  // tell the device another avail ring entry is available.
  disk.avail->idx += 1; // not % NUM ...

更新 disk.avail->idx,根據 VirtIO Spec 的描述,我們不可以對其取模 ( modulo )。


  __sync_synchronize();

這個 memory barrier 可以保證,在我們 notify the queue 之前,我們已經完成了對 disk.avil->idx 的寫入。


  *R(VIRTIO_MMIO_QUEUE_NOTIFY) = 0; // value is queue number

0 代表要通知 virtqueue 0,叫 virtqueue 0 起來做事了。看 disk.avail->ring 裡面,有沒有新的 descriptor 需要處理 ( 可以藉由 disk.avail->idx 得到這個資訊 ) 。有的話,就處理相對應的 descriptor。


  // Wait for virtio_disk_intr() to say request has finished.
  while(b->disk == 1) {
    sleep(b, &disk.vdisk_lock);
  }

等待 virtio_disk_intr() 的通知,也就是等待 VirtIO block device 處理相對應的請求。
假如 b->disk = 1 ( 表示 VirtIO Block Device 還在處理 ),就讓該 process 陷入睡眠。


  disk.info[idx[0]].b = 0;
  free_chain(idx[0]);
  release(&disk.vdisk_lock);
}

一旦醒來了,表示 VirtIO block device 的請求已經被完成了,可以把資源釋放掉。

  • free_chain : 把 idx[0] 為始的 chain of descriptors 通通 free 掉
  • release : 釋放這個 spin_lock


kernel/virtio_disk.c/virtio_disk_intr

void
virtio_disk_intr()
{
  acquire(&disk.vdisk_lock);

代表每次只有一個 CPU 可以進行 VirtIO block device 的操作。


  // the device won't raise another interrupt until we tell it
  // we've seen this interrupt, which the following line does.
  // this may race with the device writing new entries to
  // the "used" ring, in which case we may process the new
  // completion entries in this interrupt, and have nothing to do
  // in the next interrupt, which is harmless.
  *R(VIRTIO_MMIO_INTERRUPT_ACK) = *R(VIRTIO_MMIO_INTERRUPT_STATUS) & 0x3;

當我們沒有 acknowledge interrupt 的話,這個 device 就不會發新的一發 interrupt。
這裡就是在做 acknowledge interrupt 的動作。

  • 讀取 VIRTIO_MMIO_INTERRUPT_STATUS 可以知道目前發生 interrupt 的原因
    • Used Ring Update - bit 0 : 這個 interrupt 會被 asserted,是因為 VirtIO Device 已經更新了至少一個 active 的 virtqueue 的 disk.used 的 ring。
    • Configuration Change - bit 1 : 當 device 的 configuration 發生改變的時候,會發起這個 bit。
      • TODO : 目前不知道什麼情況會導致 configuration 改變 …
  • 把相對應的 bit 寫入 VIRTIO_MMIO_INTERRUPT_ACK,表示我們 acknowledge 這個 interrupt 了,讓 device 可以發起下一發 interrupt 。

  __sync_synchronize();

這個 memory barrier 確保我們對 interrupt 的 acknowledgement 已經傳給硬體之後,CPU 才嘗試對 disk 相關的資料進行讀取 ( disk.used, disk.used_idx … )。


  // the device increments disk.used->idx when it
  // adds an entry to the used ring.

  while(disk.used_idx != disk.used->idx){
  • 這個 while loop 會用來檢查哪些 request 是已經完成的。
  • disk.used->idx 這個值會被 VirtIO Block Device 寫入,這個值會被單調遞增 ( 根據 VirtIO spec,不可被 modulo ! 要軟體自己來 modulo )
  • disk.used_idx 是由 software ( xv6-riscv ) 來維護,VirtIO Block Device 不會去使用這個值 ( 不會讀取,也不會寫入 )。讓軟體來追蹤,目前軟體處理到哪裡。
  • 只要 disk.used_idx != disk.used->idx 不相等,表示有更多 request 是 VirtIO Block Device 已經處理好,但軟體還沒處理的 request。

    __sync_synchronize();

確保我們在使用 disk.used->ring 的資訊之前,其他的記憶體操作都已經完成了。


    int id = disk.used->ring[disk.used_idx % NUM].id;
  • id 是指 disk.desc 陣列的 index。
  • 該 descriptor 已經被 VirtIO block device 處理完了。假如是 chain of descriptors ,表示所有 descriptors 都處理完了。
  • 該 descriptor 還沒有被軟體 ( driver ) 處理。

    if(disk.info[id].status != 0)
      panic("virtio_disk_intr status");

假如 VirtIO Block Device 對 status 寫入的值不是 0,代表這次的 request 是失敗的。


    struct buf *b = disk.info[id].b;

從 disk.desc 陣列的 index ( id ) 取得相對應的 struct buf *b


    b->disk = 0;   // disk is done with buf

這代表 VirtIO Block Device 已經完成了這個請求。


    wakeup(b);

喚醒因 b ( channel 為 b ) 而睡覺的 process。


    disk.used_idx += 1;
  }

處理下一個 VirtIO Block Device 已經完成,但 driver 還沒處理的 request。



Reference


圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言