Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

virtio cleanup #5

Open
wants to merge 2 commits into
base: xv6-riscv-fall19
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 40 additions & 36 deletions kernel/virtio.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,53 +10,57 @@

// virtio mmio control registers, mapped starting at 0x10001000.
// from qemu virtio_mmio.h
#define VIRTIO_MMIO_MAGIC_VALUE 0x000 // 0x74726976
#define VIRTIO_MMIO_VERSION 0x004 // version; 1 is legacy
#define VIRTIO_MMIO_DEVICE_ID 0x008 // device type; 1 is net, 2 is disk
#define VIRTIO_MMIO_VENDOR_ID 0x00c // 0x554d4551
#define VIRTIO_MMIO_DEVICE_FEATURES 0x010
#define VIRTIO_MMIO_DRIVER_FEATURES 0x020
#define VIRTIO_MMIO_GUEST_PAGE_SIZE 0x028 // page size for PFN, write-only
#define VIRTIO_MMIO_QUEUE_SEL 0x030 // select queue, write-only
#define VIRTIO_MMIO_QUEUE_NUM_MAX 0x034 // max size of current queue, read-only
#define VIRTIO_MMIO_QUEUE_NUM 0x038 // size of current queue, write-only
#define VIRTIO_MMIO_QUEUE_ALIGN 0x03c // used ring alignment, write-only
#define VIRTIO_MMIO_QUEUE_PFN 0x040 // physical page number for queue, read/write
#define VIRTIO_MMIO_QUEUE_READY 0x044 // ready bit
#define VIRTIO_MMIO_QUEUE_NOTIFY 0x050 // write-only
#define VIRTIO_MMIO_INTERRUPT_STATUS 0x060 // read-only
#define VIRTIO_MMIO_INTERRUPT_ACK 0x064 // write-only
#define VIRTIO_MMIO_STATUS 0x070 // read/write
#define VIRTIO_MMIO_MAGIC_VALUE 0x000 // 0x74726976
#define VIRTIO_MMIO_VERSION 0x004 // version; 1 is legacy
#define VIRTIO_MMIO_DEVICE_ID 0x008 // device type; 1 is net, 2 is disk
#define VIRTIO_MMIO_VENDOR_ID 0x00c // 0x554d4551
#define VIRTIO_MMIO_DEVICE_FEATURES 0x010
#define VIRTIO_MMIO_DRIVER_FEATURES 0x020
#define VIRTIO_MMIO_GUEST_PAGE_SIZE 0x028 // page size for PFN, write-only
#define VIRTIO_MMIO_QUEUE_SEL 0x030 // select queue, write-only
#define VIRTIO_MMIO_QUEUE_NUM_MAX 0x034 // max size of current queue, read-only
#define VIRTIO_MMIO_QUEUE_NUM 0x038 // size of current queue, write-only
#define VIRTIO_MMIO_QUEUE_ALIGN 0x03c // used ring alignment, write-only
#define VIRTIO_MMIO_QUEUE_PFN 0x040 // physical page number for queue, read/write
#define VIRTIO_MMIO_QUEUE_READY 0x044 // ready bit
#define VIRTIO_MMIO_QUEUE_NOTIFY 0x050 // write-only
#define VIRTIO_MMIO_INTERRUPT_STATUS 0x060 // read-only
#define VIRTIO_MMIO_INTERRUPT_ACK 0x064 // write-only
#define VIRTIO_MMIO_STATUS 0x070 // read/write
#define VIRTIO_MMIO_CONFIG 0x100 // configuration space

// status register bits, from qemu virtio_config.h
#define VIRTIO_CONFIG_S_ACKNOWLEDGE 1
#define VIRTIO_CONFIG_S_DRIVER 2
#define VIRTIO_CONFIG_S_DRIVER_OK 4
#define VIRTIO_CONFIG_S_FEATURES_OK 8
#define VIRTIO_CONFIG_S_ACKNOWLEDGE 1
#define VIRTIO_CONFIG_S_DRIVER 2
#define VIRTIO_CONFIG_S_DRIVER_OK 4
#define VIRTIO_CONFIG_S_FEATURES_OK 8

// device feature bits
#define VIRTIO_BLK_F_RO 5 /* Disk is read-only */
#define VIRTIO_BLK_F_SCSI 7 /* Supports scsi command passthru */
#define VIRTIO_BLK_F_CONFIG_WCE 11 /* Writeback mode available in config */
#define VIRTIO_BLK_F_MQ 12 /* support more than one vq */
#define VIRTIO_BLK_F_RO 5 /* Disk is read-only */
#define VIRTIO_BLK_F_SCSI 7 /* Supports scsi command passthru */
#define VIRTIO_BLK_F_CONFIG_WCE 11 /* Writeback mode available in config */
#define VIRTIO_BLK_F_MQ 12 /* support more than one vq */
#define VIRTIO_F_ANY_LAYOUT 27
#define VIRTIO_RING_F_INDIRECT_DESC 28
#define VIRTIO_RING_F_EVENT_IDX 29

// this many virtio descriptors.
// must be a power of two.
#define NUM 8

struct VRingDesc {
struct virtq_desc {
uint64 addr;
uint32 len;
uint16 flags;
uint16 next;
};
#define VRING_DESC_F_NEXT 1 // chained with another descriptor
#define VRING_DESC_F_WRITE 2 // device writes (vs read)
#define VIRTQ_DESC_F_NEXT 1 // chained with another descriptor
#define VIRTQ_DESC_F_WRITE 2 // device writes (vs read)

struct virtq_avail {
uint16 flags;
uint16 idx;
uint16 ring[];
};
#define VIRTQ_AVAIL_F_NO_INTERRUPT 1 // suppress interrupts

struct VRingUsedElem {
struct virtq_used_elem {
uint32 id; // index of start of completed descriptor chain
uint32 len;
};
Expand All @@ -65,8 +69,8 @@ struct VRingUsedElem {
#define VIRTIO_BLK_T_IN 0 // read the disk
#define VIRTIO_BLK_T_OUT 1 // write the disk

struct UsedArea {
struct virtq_used {
uint16 flags;
uint16 id;
struct VRingUsedElem elems[NUM];
uint16 idx;
struct virtq_used_elem ring[];
};
45 changes: 24 additions & 21 deletions kernel/virtio_disk.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,24 @@
// the address of virtio mmio register r.
#define R(n, r) ((volatile uint32 *)(VIRTION(n) + (r)))

// this many virtio descriptors.
// must be a power of two.
#define NUM 8

struct disk {
// memory for virtio descriptors &c for queue 0.
// this is a global instead of allocated because it has
// to be multiple contiguous pages, which kalloc()
// doesn't support.
char pages[2*PGSIZE];

struct VRingDesc *desc;
uint16 *avail;
struct UsedArea *used;
struct virtq_desc *desc;
struct virtq_avail *avail;
struct virtq_used *used;

// our own book-keeping.
char free[NUM]; // is a descriptor free?
uint16 used_idx; // we've looked this far in used[2..NUM].
uint16 used_idx; // we've looked this far in used->ring.

// track info about in-flight operations,
// for use when completion interrupt arrives.
Expand Down Expand Up @@ -109,13 +113,13 @@ virtio_disk_init(int n)
memset(disk[n].pages, 0, sizeof(disk[n].pages));
*R(n, VIRTIO_MMIO_QUEUE_PFN) = ((uint64)disk[n].pages) >> PGSHIFT;

// desc = pages -- num * VRingDesc
// desc = pages -- num * virtq_desc
// avail = pages + 0x40 -- 2 * uint16, then num * uint16
// used = pages + 4096 -- 2 * uint16, then num * vRingUsedElem
// used = pages + 4096 -- 2 * uint16, then num * virtq_used_elem

disk[n].desc = (struct VRingDesc *) disk[n].pages;
disk[n].avail = (uint16*)(((char*)disk[n].desc) + NUM*sizeof(struct VRingDesc));
disk[n].used = (struct UsedArea *) (disk[n].pages + PGSIZE);
disk[n].desc = (struct virtq_desc *) disk[n].pages;
disk[n].avail = (struct virtq_avail *)(((char*)disk[n].desc) + NUM*sizeof(struct virtq_desc));
disk[n].used = (struct virtq_used *) (disk[n].pages + PGSIZE);

for(int i = 0; i < NUM; i++)
disk[n].free[i] = 1;
Expand Down Expand Up @@ -156,7 +160,7 @@ free_chain(int n, int i)
{
while(1){
free_desc(n, i);
if(disk[n].desc[i].flags & VRING_DESC_F_NEXT)
if(disk[n].desc[i].flags & VIRTQ_DESC_F_NEXT)
i = disk[n].desc[i].next;
else
break;
Expand Down Expand Up @@ -217,35 +221,34 @@ virtio_disk_rw(int n, struct buf *b, int write)
// thus the call to kvmpa().
disk[n].desc[idx[0]].addr = (uint64) kvmpa((uint64) &buf0);
disk[n].desc[idx[0]].len = sizeof(buf0);
disk[n].desc[idx[0]].flags = VRING_DESC_F_NEXT;
disk[n].desc[idx[0]].flags = VIRTQ_DESC_F_NEXT;
disk[n].desc[idx[0]].next = idx[1];

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

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

// record struct buf for virtio_disk_intr().
b->disk = 1;
disk[n].info[idx[0]].b = b;

// avail[0] is flags
// avail[1] tells the device how far to look in avail[2...].
// avail[2...] are desc[] indices the device should process.
// avail->idx tells the device how far to look in avail->ring.
// avail->ring[...] are desc[] indices the device should process.
// we only tell device the first index in our chain of descriptors.
disk[n].avail[2 + (disk[n].avail[1] % NUM)] = idx[0];
disk[n].avail->ring[disk[n].avail->idx % NUM] = idx[0];
__sync_synchronize();
disk[n].avail[1] = disk[n].avail[1] + 1;
disk[n].avail->idx += 1;

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

Expand All @@ -265,8 +268,8 @@ virtio_disk_intr(int n)
{
acquire(&disk[n].vdisk_lock);

while((disk[n].used_idx % NUM) != (disk[n].used->id % NUM)){
int id = disk[n].used->elems[disk[n].used_idx].id;
while((disk[n].used_idx % NUM) != (disk[n].used->idx % NUM)){
int id = disk[n].used->ring[disk[n].used_idx].id;

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