Wrap state of disk driver in a struct so that it is easier to support
several disks (e.g., in a mount lab).
This commit is contained in:
parent
d96a8c5661
commit
e398a9815c
|
@ -20,36 +20,38 @@
|
||||||
// the address of virtio mmio register r.
|
// the address of virtio mmio register r.
|
||||||
#define R(r) ((volatile uint32 *)(VIRTIO0 + (r)))
|
#define R(r) ((volatile uint32 *)(VIRTIO0 + (r)))
|
||||||
|
|
||||||
struct spinlock vdisk_lock;
|
static struct disk {
|
||||||
|
// memory for virtio descriptors &c for queue 0.
|
||||||
|
// this is a global instead of allocated because it must
|
||||||
|
// be multiple contiguous pages, which kalloc()
|
||||||
|
// doesn't support, and page aligned.
|
||||||
|
char pages[2*PGSIZE];
|
||||||
|
struct VRingDesc *desc;
|
||||||
|
uint16 *avail;
|
||||||
|
struct UsedArea *used;
|
||||||
|
|
||||||
// memory for virtio descriptors &c for queue 0.
|
// our own book-keeping.
|
||||||
// this is a global instead of allocated because it has
|
char free[NUM]; // is a descriptor free?
|
||||||
// to be multiple contiguous pages, which kalloc()
|
uint16 used_idx; // we've looked this far in used[2..NUM].
|
||||||
// doesn't support.
|
|
||||||
__attribute__ ((aligned (PGSIZE)))
|
|
||||||
static char pages[2*PGSIZE];
|
|
||||||
static struct VRingDesc *desc;
|
|
||||||
static uint16 *avail;
|
|
||||||
static struct UsedArea *used;
|
|
||||||
|
|
||||||
// our own book-keeping.
|
// track info about in-flight operations,
|
||||||
static char free[NUM]; // is a descriptor free?
|
// for use when completion interrupt arrives.
|
||||||
static uint16 used_idx; // we've looked this far in used[2..NUM].
|
// indexed by first descriptor index of chain.
|
||||||
|
struct {
|
||||||
|
struct buf *b;
|
||||||
|
char status;
|
||||||
|
} info[NUM];
|
||||||
|
|
||||||
// track info about in-flight operations,
|
struct spinlock vdisk_lock;
|
||||||
// for use when completion interrupt arrives.
|
|
||||||
// indexed by first descriptor index of chain.
|
} __attribute__ ((aligned (PGSIZE))) disk;
|
||||||
static struct {
|
|
||||||
struct buf *b;
|
|
||||||
char status;
|
|
||||||
} info[NUM];
|
|
||||||
|
|
||||||
void
|
void
|
||||||
virtio_disk_init(void)
|
virtio_disk_init(void)
|
||||||
{
|
{
|
||||||
uint32 status = 0;
|
uint32 status = 0;
|
||||||
|
|
||||||
initlock(&vdisk_lock, "virtio_disk");
|
initlock(&disk.vdisk_lock, "virtio_disk");
|
||||||
|
|
||||||
if(*R(VIRTIO_MMIO_MAGIC_VALUE) != 0x74726976 ||
|
if(*R(VIRTIO_MMIO_MAGIC_VALUE) != 0x74726976 ||
|
||||||
*R(VIRTIO_MMIO_VERSION) != 1 ||
|
*R(VIRTIO_MMIO_VERSION) != 1 ||
|
||||||
|
@ -93,19 +95,19 @@ virtio_disk_init(void)
|
||||||
if(max < NUM)
|
if(max < NUM)
|
||||||
panic("virtio disk max queue too short");
|
panic("virtio disk max queue too short");
|
||||||
*R(VIRTIO_MMIO_QUEUE_NUM) = NUM;
|
*R(VIRTIO_MMIO_QUEUE_NUM) = NUM;
|
||||||
memset(pages, 0, sizeof(pages));
|
memset(disk.pages, 0, sizeof(disk.pages));
|
||||||
*R(VIRTIO_MMIO_QUEUE_PFN) = ((uint64)pages) >> PGSHIFT;
|
*R(VIRTIO_MMIO_QUEUE_PFN) = ((uint64)disk.pages) >> PGSHIFT;
|
||||||
|
|
||||||
// desc = pages -- num * VRingDesc
|
// desc = pages -- num * VRingDesc
|
||||||
// avail = pages + 0x40 -- 2 * uint16, then num * uint16
|
// avail = pages + 0x40 -- 2 * uint16, then num * uint16
|
||||||
// used = pages + 4096 -- 2 * uint16, then num * vRingUsedElem
|
// used = pages + 4096 -- 2 * uint16, then num * vRingUsedElem
|
||||||
|
|
||||||
desc = (struct VRingDesc *) pages;
|
disk.desc = (struct VRingDesc *) disk.pages;
|
||||||
avail = (uint16*)(((char*)desc) + NUM*sizeof(struct VRingDesc));
|
disk.avail = (uint16*)(((char*)disk.desc) + NUM*sizeof(struct VRingDesc));
|
||||||
used = (struct UsedArea *) (pages + PGSIZE);
|
disk.used = (struct UsedArea *) (disk.pages + PGSIZE);
|
||||||
|
|
||||||
for(int i = 0; i < NUM; i++)
|
for(int i = 0; i < NUM; i++)
|
||||||
free[i] = 1;
|
disk.free[i] = 1;
|
||||||
|
|
||||||
// plic.c and trap.c arrange for interrupts from VIRTIO0_IRQ.
|
// plic.c and trap.c arrange for interrupts from VIRTIO0_IRQ.
|
||||||
}
|
}
|
||||||
|
@ -115,8 +117,8 @@ static int
|
||||||
alloc_desc()
|
alloc_desc()
|
||||||
{
|
{
|
||||||
for(int i = 0; i < NUM; i++){
|
for(int i = 0; i < NUM; i++){
|
||||||
if(free[i]){
|
if(disk.free[i]){
|
||||||
free[i] = 0;
|
disk.free[i] = 0;
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -129,11 +131,11 @@ free_desc(int i)
|
||||||
{
|
{
|
||||||
if(i >= NUM)
|
if(i >= NUM)
|
||||||
panic("virtio_disk_intr 1");
|
panic("virtio_disk_intr 1");
|
||||||
if(free[i])
|
if(disk.free[i])
|
||||||
panic("virtio_disk_intr 2");
|
panic("virtio_disk_intr 2");
|
||||||
desc[i].addr = 0;
|
disk.desc[i].addr = 0;
|
||||||
free[i] = 1;
|
disk.free[i] = 1;
|
||||||
wakeup(&free[0]);
|
wakeup(&disk.free[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// free a chain of descriptors.
|
// free a chain of descriptors.
|
||||||
|
@ -142,8 +144,8 @@ free_chain(int i)
|
||||||
{
|
{
|
||||||
while(1){
|
while(1){
|
||||||
free_desc(i);
|
free_desc(i);
|
||||||
if(desc[i].flags & VRING_DESC_F_NEXT)
|
if(disk.desc[i].flags & VRING_DESC_F_NEXT)
|
||||||
i = desc[i].next;
|
i = disk.desc[i].next;
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -168,7 +170,7 @@ virtio_disk_rw(struct buf *b, int write)
|
||||||
{
|
{
|
||||||
uint64 sector = b->blockno * (BSIZE / 512);
|
uint64 sector = b->blockno * (BSIZE / 512);
|
||||||
|
|
||||||
acquire(&vdisk_lock);
|
acquire(&disk.vdisk_lock);
|
||||||
|
|
||||||
// the spec says that legacy block operations use three
|
// the spec says that legacy block operations use three
|
||||||
// descriptors: one for type/reserved/sector, one for
|
// descriptors: one for type/reserved/sector, one for
|
||||||
|
@ -180,7 +182,7 @@ virtio_disk_rw(struct buf *b, int write)
|
||||||
if(alloc3_desc(idx) == 0) {
|
if(alloc3_desc(idx) == 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
sleep(&free[0], &vdisk_lock);
|
sleep(&disk.free[0], &disk.vdisk_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
// format the three descriptors.
|
// format the three descriptors.
|
||||||
|
@ -201,67 +203,67 @@ virtio_disk_rw(struct buf *b, int write)
|
||||||
|
|
||||||
// buf0 is on a kernel stack, which is not direct mapped,
|
// buf0 is on a kernel stack, which is not direct mapped,
|
||||||
// thus the call to kvmpa().
|
// thus the call to kvmpa().
|
||||||
desc[idx[0]].addr = (uint64) kvmpa((uint64) &buf0);
|
disk.desc[idx[0]].addr = (uint64) kvmpa((uint64) &buf0);
|
||||||
desc[idx[0]].len = sizeof(buf0);
|
disk.desc[idx[0]].len = sizeof(buf0);
|
||||||
desc[idx[0]].flags = VRING_DESC_F_NEXT;
|
disk.desc[idx[0]].flags = VRING_DESC_F_NEXT;
|
||||||
desc[idx[0]].next = idx[1];
|
disk.desc[idx[0]].next = idx[1];
|
||||||
|
|
||||||
desc[idx[1]].addr = (uint64) b->data;
|
disk.desc[idx[1]].addr = (uint64) b->data;
|
||||||
desc[idx[1]].len = BSIZE;
|
disk.desc[idx[1]].len = BSIZE;
|
||||||
if(write)
|
if(write)
|
||||||
desc[idx[1]].flags = 0; // device reads b->data
|
disk.desc[idx[1]].flags = 0; // device reads b->data
|
||||||
else
|
else
|
||||||
desc[idx[1]].flags = VRING_DESC_F_WRITE; // device writes b->data
|
disk.desc[idx[1]].flags = VRING_DESC_F_WRITE; // device writes b->data
|
||||||
desc[idx[1]].flags |= VRING_DESC_F_NEXT;
|
disk.desc[idx[1]].flags |= VRING_DESC_F_NEXT;
|
||||||
desc[idx[1]].next = idx[2];
|
disk.desc[idx[1]].next = idx[2];
|
||||||
|
|
||||||
info[idx[0]].status = 0;
|
disk.info[idx[0]].status = 0;
|
||||||
desc[idx[2]].addr = (uint64) &info[idx[0]].status;
|
disk.desc[idx[2]].addr = (uint64) &disk.info[idx[0]].status;
|
||||||
desc[idx[2]].len = 1;
|
disk.desc[idx[2]].len = 1;
|
||||||
desc[idx[2]].flags = VRING_DESC_F_WRITE; // device writes the status
|
disk.desc[idx[2]].flags = VRING_DESC_F_WRITE; // device writes the status
|
||||||
desc[idx[2]].next = 0;
|
disk.desc[idx[2]].next = 0;
|
||||||
|
|
||||||
// record struct buf for virtio_disk_intr().
|
// record struct buf for virtio_disk_intr().
|
||||||
b->disk = 1;
|
b->disk = 1;
|
||||||
info[idx[0]].b = b;
|
disk.info[idx[0]].b = b;
|
||||||
|
|
||||||
// avail[0] is flags
|
// avail[0] is flags
|
||||||
// avail[1] tells the device how far to look in avail[2...].
|
// avail[1] tells the device how far to look in avail[2...].
|
||||||
// avail[2...] are desc[] indices the device should process.
|
// avail[2...] are desc[] indices the device should process.
|
||||||
// we only tell device the first index in our chain of descriptors.
|
// we only tell device the first index in our chain of descriptors.
|
||||||
avail[2 + (avail[1] % NUM)] = idx[0];
|
disk.avail[2 + (disk.avail[1] % NUM)] = idx[0];
|
||||||
__sync_synchronize();
|
__sync_synchronize();
|
||||||
avail[1] = avail[1] + 1;
|
disk.avail[1] = disk.avail[1] + 1;
|
||||||
|
|
||||||
*R(VIRTIO_MMIO_QUEUE_NOTIFY) = 0; // value is queue number
|
*R(VIRTIO_MMIO_QUEUE_NOTIFY) = 0; // value is queue number
|
||||||
|
|
||||||
// Wait for virtio_disk_intr() to say request has finished.
|
// Wait for virtio_disk_intr() to say request has finished.
|
||||||
while(b->disk == 1) {
|
while(b->disk == 1) {
|
||||||
sleep(b, &vdisk_lock);
|
sleep(b, &disk.vdisk_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
info[idx[0]].b = 0;
|
disk.info[idx[0]].b = 0;
|
||||||
free_chain(idx[0]);
|
free_chain(idx[0]);
|
||||||
|
|
||||||
release(&vdisk_lock);
|
release(&disk.vdisk_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
virtio_disk_intr()
|
virtio_disk_intr()
|
||||||
{
|
{
|
||||||
acquire(&vdisk_lock);
|
acquire(&disk.vdisk_lock);
|
||||||
|
|
||||||
while((used_idx % NUM) != (used->id % NUM)){
|
while((disk.used_idx % NUM) != (disk.used->id % NUM)){
|
||||||
int id = used->elems[used_idx].id;
|
int id = disk.used->elems[disk.used_idx].id;
|
||||||
|
|
||||||
if(info[id].status != 0)
|
if(disk.info[id].status != 0)
|
||||||
panic("virtio_disk_intr status");
|
panic("virtio_disk_intr status");
|
||||||
|
|
||||||
info[id].b->disk = 0; // disk is done with buf
|
disk.info[id].b->disk = 0; // disk is done with buf
|
||||||
wakeup(info[id].b);
|
wakeup(disk.info[id].b);
|
||||||
|
|
||||||
used_idx = (used_idx + 1) % NUM;
|
disk.used_idx = (disk.used_idx + 1) % NUM;
|
||||||
}
|
}
|
||||||
|
|
||||||
release(&vdisk_lock);
|
release(&disk.vdisk_lock);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue