Skip to content

Transfer Monitoring

Jaroslav Jindrak edited this page Jan 21, 2018 · 9 revisions

To demonstrate and benchmark performance of the xHCI stack, a proprietary subsystem has been implemented in HelenOS. The primary function of this subsystem is to communicate with a custom QEMU USB device over arbitrary endpoints and provide statistical information related to the communication. This way, it can be used to verify the correctness of message transmission, experiment with synchronization and to measure performance indicators.

In addition, since the subsystem is built on top of the USB device driver framework and has no xHCI-specific requirements, it can be also used to compare parameters of the xHCI stack with its predecessors.

The subsystem is composed of three parts:

QEMU fork with a proprietary diagnostic device (usb-tmon)
This implementation of QEMU contains a virtual USB device, which carries the diagnostic device class descriptor.
USB Diagnostic Device Driver (usbdiag)
The usbdiag driver matches with the QEMU diagnostic device and facilitates all communication with it. It also exposes a remote interface for all HelenOS aplications.
User Frontend Program (tmon)
The tmon program is the primary user frontend in shell. It can use the interface exposed by usbdiag to perform various tests with diagnostic devices and return human-readable results.

QEMU Device

Overview

The usb-tmon virtual device is a diagnostic class USB device created in order to easily test the functionality of the xHCI stack. It contains two sets of endpoints, each of which contains six endpoints (one for each transfer direction for each type of transfer - i.e. interrupt, bulk and isochronous). The first set of endpoints (0x1 - 0x6) only monitors the size of the data it receives or sends but does not perform any check for validity when it receives data nor does it actually send any data back (it only tells QEMU to use its buffer as it is as the result). The second set of endpoints (0x7 - 0xC) fills the packets it sends to the host with a repeated predefined value (0xDEADBEEF) and checks the packets it receives that it contains the same repeated value.

Monitoring

The information gathered and displayed depends on the type of transfer the device receives:

Interrupt
On every IN or OUT data request, the device outputs to QEMU's standard output the time (in microseconds) since the last transfer request of the same type.
Bulk
On every bulk transfer request, the device checks the time since the last request of the same type, if the time is over a second, the device outputs to QEMU's standard output the amount of bytes transferred in that time period.
Isochronous
Currently only outputs to QEMU's standard output a message notifying the user that a transfer of this type has been received.

Source code

The implementation of this device is located in the file hw/usb/dev-tmon.c in the helenos-xhci-team/qemu fork of the official QEMU repository. It contains several key structures and functions:

USBTmonState
Structure that reperesents the current state of a `usb-tmon` device.
desc_tmon, desc_device_tmon, desc_iface_tmon
These three structures form the descriptor of the device and contain information about the device's class, protocol, endpoints etc.
usb_tmon_class_init
Called when QEMU starts, so it is used as a constructor function for the virtual device and all relevant data.
usb_tmon_realize
Called when an instance of the `usb-tmon` device gets created and is used to initialize a specific instance of `USBTmonState`.
usb_tmon_handle_attach
Called when an instance of the `usb-tmon` device get attached to the guest OS.
usb_tmon_handle_control
Called when the device receives a control request, in its current implementation simply forwards the request to QEMU via `usb_desc_handle_control`.
usb_tmon_handle_data
Called when the device receives an interrupt, a bulk or an isochronous data request, determines the receiving endpoint, stores information about handled data and if needed, sends or validates a USB packet.
usb_tmon_{int|bulk|isoc}_{in|out}
Called on specific kinds of transfers and track sent/received data.

These are the structures and functions one needs to modify in order to modify the behavior of the device. Additionally, the source code contains helper functions (e.g. time measurement with get_now_sec and get_now_usec) and QEMU debuggin/informational functions and structures (e.g. desc_strings, vmstate_usb_tmon, usb_tmon_info and usb_tmon_register_types). These should seldom require modification.

For the purposes of modifying or debugging usb-tmon's source code, the header include/hw/usb.h contains most of the structure definitions and function declarations that might be needed.

Usage

In a running instance of QEMU, one can press the C-A-2 key combination to switch to the monitor in which they can type device_add usb-tmon to attach a new usb-tmon device (it is possible to redirect the monitor to QEMU's standard IO by adding -monitor stdioto its startup command). To have QEMU start with usb-tmon attached, they may add -device usb-tmon to their QEMU startup command.

Driver

TODO @petrmanek

Frontend

Structure

This section explains what you can find in the uspace/app/tmon directory.

main.c
Main entry point, command selection and usage string.
commands.h
Executable commands.
list.c
Implementation of the `list` command.
tf.h, tf.c
Testing framework, common code for all `test-*` commands.
resolve.h, resolve.c
Resolving DDF device from string using devman's IPC interface.
burst_tests.c
Implementation of burst tests.

Usage

tmon: benchmark USB diagnostic device

Usage: tmon command [device] [options]

      list - Print a list of connected diagnostic devices.
      test-intr-in - Read from interrupt endpoint as fast as possible.
      test-intr-out - Write to interrupt endpoint as fast as possible.
      test-bulk-in - Read from bulk endpoint as fast as possible.
      test-bulk-out - Write to bulk endpoint as fast as possible.
      test-isoch-in - Read from isochronous endpoint as fast as possible.
      test-isoch-out - Write to isochronous endpoint as fast as possible.

      -n --cycles
            Set the number of read/write cycles.
      -s --size
            Set the data size transferred in a single cycle.

If no device is specified, the first device is used provided that it is the only one connected. Otherwise, the command fails.