Hacker News new | ask | show | jobs
by jmillikin 1396 days ago
Because different use cases require different designs? If you try to create a protocol that can work for all purposes, it'll be a poor fit for any of them and will be out-competed by more specialized alternatives.

There's a reason emulators design their virtual devices to resemble real hardware (PCI, SCSI, USB) -- there's already going to be a bunch of code in the hypervisor to create fake hardware. It's also more practical to piggy-back on PCI (etc) when the spec needs to be implemented by competing vendors, since there's no kernel and no OS idioms involved. Not to mention various pre-kernel code such as EFI and bootloaders.

Conversely, userspace developers really do not want to be coding up a fake PCI device with registers and interrupts and so on just to get some bytes into the kernel. They want to invoke system calls (ioctl, mmap, io_uring) and let the OS handle the details.

1 comments

The basis of virtio is literally just a ring queue, with its request descriptors looking almost exactly like structs suitable for passing to readv(2) or writev(2); the PCI shim is built on top of that and is completely optional (you can have a purely MMIO virtio device, after all). It was built this way so that KVM would not have to mimic the idiosyncrasies of real hardware: passing data as-is to the physical devices can't work for obvious reasons (even if you disregard security completely); instead in can, after minimal processing, shove it into the Linux kernel and let it take care of the rest.
The VirtIO specification for its use with MMIO[0] contains the following example device description:

  // EXAMPLE: virtio_block device taking 512 bytes at 0x1e000, interrupt 42. 
  virtio_block@1e000 { 
          compatible = "virtio,mmio"; 
          reg = <0x1e000 0x200>; 
          interrupts = <42>; 
  }
The next sub-section of the MMIO section is a datasheet of control registers.

My point about PCI isn't strictly about PCI, it applies equally to VirtIO over MMIO. I do not ever want to have my userspace code poke at memory-mapped registers or do interrupt handling just to do the equivalent of an ioctl.

---

OK, fine, maybe PCI and MMIO are irrelevant but there could be opportunities to share struct layouts. In the section describing block devices[1], there's some code listings for the request packets. A representative example is the request struct:

  struct virtio_blk_req { 
          le32 type; 
          le32 reserved; 
          le64 sector; 
          u8 data[][512]; 
          u8 status; 
  };
Take a look at that request, then look at struct ublksrv_ctrl_cmd in ublk_cmd.h[2]. There is very little the two protocols have in common. Yes, they're both doing some sort of packetized data transfer, but all of the details are different.

Also, just ... just look at the size of the VirtIO specification. There is a lot there. I haven't run a `wc -l` but it would not surprise me if just the spec for VirtIO is longer than the entire patch series for ublk. Out of all that, the sum total of the virtio-blk struct layouts is something like 100, 200 lines.

Is it worth going through the trouble of trying to unify these two unrelated specs just so that we can satisfy some bizarre philosophical goal of carefully avoiding new ideas?

Like, if you're going to go that far, why does virtio-blk need to exist instead of continuing to emulate SCSI? Or using iSCSI for host<-> device transfer? The obvious answer is, again, because different use cases have different requirements. It's silly to cook two soups in the same bowl.

[0] http://docs.oasis-open.org/virtio/virtio/v1.0/cs04/virtio-v1...

[1] https://docs.oasis-open.org/virtio/virtio/v1.1/csprd01/virti...

[2] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/lin...