Virtio-serial API: Difference between revisions

From KVM
mNo edit summary
(→‎Guest API: add links to guest agent src code for reference)
 
(17 intermediate revisions by 2 users not shown)
Line 7: Line 7:
|-
|-
|Port discovery
|Port discovery
|symlinks from /dev/virtio-port/<portname> to /dev/vportNpn
|symlinks from <code> /dev/virtio-port/<portname> </code> to <code> /dev/vportNpn </code> as mentioned in [http://www.linux-kvm.org/page/VMchannel_Requirements#Invocation Invocation] and [http://fedoraproject.org/wiki/Features/VirtioSerial#How_To_Test How To Test]
|
|In Windows, port enumeration can be done using SetupAPI functions. For
more information please see [http://git.kernel.org/?p=virt/kvm/kvm-guest-drivers-windows.git;a=blob_plain;f=vioserial/app/device.cpp;hb=48b3a3f78fe4711d1561b32c9a1c0ca6471f85e3#GetDevicePath GetDevicePath] or visit MSDN site [http://msdn.microsoft.com/en-us/library/cc185682%28VS.85%29.aspx#Setup_API Setup API]
|-
|-
|Opening port
|Opening port
|open(2). Returns >= 0 on success. Only one open allowed at a time for a port.
|<code>open(2)</code>
|
* Returns >= 0 on success.
* Only one open allowed at a time for a port.
|<code>HANDLE WINAPI CreateFile(
  __in      LPCTSTR lpFileName,
  __in      DWORD dwDesiredAccess,
  __in      DWORD dwShareMode,
  __in_opt  LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  __in      DWORD dwCreationDisposition,
  __in      DWORD dwFlagsAndAttributes,
  __in_opt  HANDLE hTemplateFile
);</code>
* If the function fails, the return value is INVALID_HANDLE_VALUE.
|-
|-
|Reading
|Reading
|read(2). Blocking as well as non-blocking reads available. Return 0 if host is not connected. Block or -EINTR otherwise. Return -ENODEV if port or device get hot-unplugged
|<code>read(2)</code>
|
* Blocking as well as non-blocking reads available.
* Return 0 if host is not connected.
* Block or <code>-EAGAIN</code> if data not available.
* Errno will contain <code>-ENODEV</code> if port or device get hot-unplugged
|<code>BOOL WINAPI ReadFile(
  __in        HANDLE hFile,
  __out        LPVOID lpBuffer,
  __in        DWORD nNumberOfBytesToRead,
  __out_opt    LPDWORD lpNumberOfBytesRead,
  __inout_opt  LPOVERLAPPED lpOverlapped
);</code>
* A pointer to an OVERLAPPED structure is required if the hFile parameter was opened with FILE_FLAG_OVERLAPPED, otherwise it can be NULL.
|-
|-
|Writing
|Writing
|write(2). Blocking as well as non-blocking. If host is not connected, write blocks or returns -EINTR. Return -ENODEV if port or device get hot-unplugged.
|<code>write(2)</code>
|
* Blocking as well as non-blocking.
* If host is not connected, write blocks or returns <code>-EAGAIN</code>.
* Errno will contain <code>-ENODEV</code> if port or device got hot-unplugged.
|<code>BOOL WINAPI WriteFile(
  __in        HANDLE hFile,
  __in        LPCVOID lpBuffer,
  __in        DWORD nNumberOfBytesToWrite,
  __out_opt    LPDWORD lpNumberOfBytesWritten,
  __inout_opt  LPOVERLAPPED lpOverlapped
);</code>
* A pointer to an OVERLAPPED structure is required if the hFile parameter was opened with FILE_FLAG_OVERLAPPED, otherwise this parameter can be NULL.
|-
|-
|Poll
|Poll
|poll(). POLLIN, POLLOUT with usual meaning. POLLHUP when host is not connected or when port or device got unplugged
|<code>poll()</code>
* <code>POLLIN, POLLOUT</code> with usual meaning.
* <code>POLLHUP</code> when host is not connected or when port or device got unplugged
|-
|-
|Signals
|Asynchronous notifications
|From kernel 2.6.37, SIGIO will be sent to guest apps that set O_ASYNC flag on the fd. SIGIO will be sent on host connection up, down and port unplug events.
|<code>signal(7)</code>
* From kernel 2.6.37, <code>SIGIO</code> will be sent to guest apps that set <code>O_ASYNC</code> flag on the fd using <code>fcntl(2)</code>.
* <code>SIGIO</code> will be sent on host connection up, down and port unplug events.
* Use <code>poll()</code> within signal handler to identify which port(s) changed state and how.
|
|
|-
|-
|}
|}


For an example of a C program that uses the virtio-serial Linux guest API, see [http://fedorapeople.org/cgit/amitshah/public_git/test-virtserial.git/tree/auto-virtserial-guest.c auto-virtserial-guest.c] and the [http://git.qemu.org/?p=qemu.git;a=blob;f=qga/channel-posix.c;hb=HEAD qemu guest agent].  The Windows guest API can be seen in action in the [http://git.qemu.org/?p=qemu.git;a=blob;f=qga/channel-win32.c;hb=HEAD qemu guest agent] as well.


== Host API ==
== Host API ==
There's an in-qemu host API exposed by the virtio-serial code. The following is true for the in-qemu API for qemu version 0.13 and for the qemu version found in Red Hat Enterprise Linux 6.0, straight from [http://git.qemu.org/?p=qemu.git;a=blob;f=hw/virtio-serial.h/virtio-serial.h;h=ff08c406819bfa62ea63ed81cd1d32e9fe18fec4;hb=stable-0.13 hw/virtio-serial.h]:
{|
| <code>
/*
  * Individual ports/apps should call this function to register the port
  * with the virtio-serial bus
  */
void virtio_serial_port_qdev_register(VirtIOSerialPortInfo *info);
/*
  * Open a connection to the port
  *  Returns 0 on success (always).
  */
int virtio_serial_open(VirtIOSerialPort *port);
/*
  * Close the connection to the port
  *  Returns 0 on success (always).
  */
int virtio_serial_close(VirtIOSerialPort *port);
/*
  * Send data to Guest
  */
ssize_t virtio_serial_write(VirtIOSerialPort *port, const uint8_t *buf,
                            size_t size);
/*
  * Query whether a guest is ready to receive data.
  */
size_t virtio_serial_guest_ready(VirtIOSerialPort *port);
/*
  * Flow control: Ports can signal to the virtio-serial core to stop
  * sending data or re-start sending data, depending on the 'throttle'
  * value here.
  */
void virtio_serial_throttle_port(VirtIOSerialPort *port, bool throttle);
</code>
|}
In addition to this, the '''VirtIOSerialPortInfo''' struct has a function pointer for a callback to be called when guest writes some data to the port:
{|
|<code>
    /*
    * Guest wrote some data to the port. This data is handed over to
    * the app via this callback.  The app is supposed to consume all
    * the data that is presented to it.
    */
    void (*have_data)(VirtIOSerialPort *port, const uint8_t *buf, size_t len);
</code>
|}
For an example use of this API, see [http://git.qemu.org/?p=qemu.git;a=blob;f=hw/virtio-serial.h/virtio-console.c;h=caea11f3a8642d7e6d6f75a91ec2a6354399e8c2;hb=stable-0.13 hw/virtio-console.c]
=== Caveats ===
* Live Migration
: When a VM uses the qemu chardev interface to talk to guest virtio-serial ports, the chardev file will be closed on the source (and may be opened on the destination).  Host applications have to be aware of such migration and either collaborate with libvirt or have their own mechanism to re-connect to the destination host and continue the communication.
: A future version of qemu may introduce 'migration notifiers' that may help chardevs let apps know of migration start / stop.
* qemu chardevs
: qemu's chardevs are notoriously out of date from the state-of-the-art and need a complete rewrite to be pleasurable to use.
: However respecting the return values from the various read/write calls to chardevs will help in ensuring data is never lost.  The various backends (unix, tcp sockets, file, etc.) do work well when used with care.

Latest revision as of 02:51, 28 June 2016

Guest API

Function Linux guest Windows guest
Port discovery symlinks from /dev/virtio-port/<portname> to /dev/vportNpn as mentioned in Invocation and How To Test In Windows, port enumeration can be done using SetupAPI functions. For

more information please see GetDevicePath or visit MSDN site Setup API

Opening port open(2)
  • Returns >= 0 on success.
  • Only one open allowed at a time for a port.
HANDLE WINAPI CreateFile(
 __in      LPCTSTR lpFileName,
 __in      DWORD dwDesiredAccess,
 __in      DWORD dwShareMode,
 __in_opt  LPSECURITY_ATTRIBUTES lpSecurityAttributes,
 __in      DWORD dwCreationDisposition,
 __in      DWORD dwFlagsAndAttributes,
 __in_opt  HANDLE hTemplateFile

);

  • If the function fails, the return value is INVALID_HANDLE_VALUE.
Reading read(2)
  • Blocking as well as non-blocking reads available.
  • Return 0 if host is not connected.
  • Block or -EAGAIN if data not available.
  • Errno will contain -ENODEV if port or device get hot-unplugged
BOOL WINAPI ReadFile(
 __in         HANDLE hFile,
 __out        LPVOID lpBuffer,
 __in         DWORD nNumberOfBytesToRead,
 __out_opt    LPDWORD lpNumberOfBytesRead,
 __inout_opt  LPOVERLAPPED lpOverlapped

);

  • A pointer to an OVERLAPPED structure is required if the hFile parameter was opened with FILE_FLAG_OVERLAPPED, otherwise it can be NULL.
Writing write(2)
  • Blocking as well as non-blocking.
  • If host is not connected, write blocks or returns -EAGAIN.
  • Errno will contain -ENODEV if port or device got hot-unplugged.
BOOL WINAPI WriteFile(
 __in         HANDLE hFile,
 __in         LPCVOID lpBuffer,
 __in         DWORD nNumberOfBytesToWrite,
 __out_opt    LPDWORD lpNumberOfBytesWritten,
 __inout_opt  LPOVERLAPPED lpOverlapped

);

  • A pointer to an OVERLAPPED structure is required if the hFile parameter was opened with FILE_FLAG_OVERLAPPED, otherwise this parameter can be NULL.
Poll poll()
  • POLLIN, POLLOUT with usual meaning.
  • POLLHUP when host is not connected or when port or device got unplugged
Asynchronous notifications signal(7)
  • From kernel 2.6.37, SIGIO will be sent to guest apps that set O_ASYNC flag on the fd using fcntl(2).
  • SIGIO will be sent on host connection up, down and port unplug events.
  • Use poll() within signal handler to identify which port(s) changed state and how.


For an example of a C program that uses the virtio-serial Linux guest API, see auto-virtserial-guest.c and the qemu guest agent. The Windows guest API can be seen in action in the qemu guest agent as well.

Host API

There's an in-qemu host API exposed by the virtio-serial code. The following is true for the in-qemu API for qemu version 0.13 and for the qemu version found in Red Hat Enterprise Linux 6.0, straight from hw/virtio-serial.h:

/*
 * Individual ports/apps should call this function to register the port
 * with the virtio-serial bus
 */
void virtio_serial_port_qdev_register(VirtIOSerialPortInfo *info);
/*
 * Open a connection to the port
 *   Returns 0 on success (always).
 */
int virtio_serial_open(VirtIOSerialPort *port);
/*
 * Close the connection to the port
 *   Returns 0 on success (always).
 */
int virtio_serial_close(VirtIOSerialPort *port);
/*
 * Send data to Guest
 */
ssize_t virtio_serial_write(VirtIOSerialPort *port, const uint8_t *buf,
                            size_t size);
/*
 * Query whether a guest is ready to receive data.
 */
size_t virtio_serial_guest_ready(VirtIOSerialPort *port);
/*
 * Flow control: Ports can signal to the virtio-serial core to stop
 * sending data or re-start sending data, depending on the 'throttle'
 * value here.
 */
void virtio_serial_throttle_port(VirtIOSerialPort *port, bool throttle);

In addition to this, the VirtIOSerialPortInfo struct has a function pointer for a callback to be called when guest writes some data to the port:

   /*
    * Guest wrote some data to the port. This data is handed over to
    * the app via this callback.  The app is supposed to consume all
    * the data that is presented to it.
    */
   void (*have_data)(VirtIOSerialPort *port, const uint8_t *buf, size_t len);

For an example use of this API, see hw/virtio-console.c

Caveats

  • Live Migration
When a VM uses the qemu chardev interface to talk to guest virtio-serial ports, the chardev file will be closed on the source (and may be opened on the destination). Host applications have to be aware of such migration and either collaborate with libvirt or have their own mechanism to re-connect to the destination host and continue the communication.
A future version of qemu may introduce 'migration notifiers' that may help chardevs let apps know of migration start / stop.
  • qemu chardevs
qemu's chardevs are notoriously out of date from the state-of-the-art and need a complete rewrite to be pleasurable to use.
However respecting the return values from the various read/write calls to chardevs will help in ensuring data is never lost. The various backends (unix, tcp sockets, file, etc.) do work well when used with care.