Introduction to the Xen Virtual Machine
First, what is the Xend daemon? It is the Xen controller daemon, meaning it handles creating new domains, destroying extant domains, migration and many other domain management tasks. A large part of its activity is based on running an HTTP server. The default port of the HTTP socket is 8000, which can be configured. Various requests for controlling the domains are handled by sending HTTP requests for domain creation, domain shutdown, domain save and restore, live migration and more. A large part of the Xend code is written in Python, and it also uses calls to C methods from within Python scripts.
We start the Xend daemon by running from the command line, after booting into Xen, xend start. What exactly does this command involve? First, Xend requires Python 2.3 to support its logging functions.
The work of the Xend daemon is based on interaction with an XCS server, the control Switch. So, when we start the Xend daemon, we check to see if the XCS is up and running. If it is not, we try to start XCS. This step is discussed more fully later in this article. .
The SrvDaemon is, in fact, the Xend main program; starting the Xend daemon creates an instance of SrvDaemon class (tools/python/xen/xend/server/SrvDaemon.py.). Two log files are created here, /var/log/xend.log and /var/log/xend-debug.log.
We next create a Channel Factory in createFactories() method. The Channel Factory has a notifier object embedded inside. Much of the work of the Xend daemon is based on messages received by this notifier. This factory creates a thread that reads the notifier in an endless loop. The notifier delegates the read request to the XCS server; see xu_notifier_read() in xen/lowlevel/xu.c. This method sends the read request to the XCS server by calling xcs_data_read().
The creation of a domain is accomplished by using a hypercall (DOM0_CREATEDOMAIN). What is a hypercall? In the Linux kernel, there is a system call with which a user space can call a method in the kernel; this is done by an interrupt (Int 0x80). In Xen, the analogous call is a hypervisor call, through which domain 0 calls a method in the hypervisor. This also is accomplished by an interrupt (Int 0x82). The hypervisor accesses each domain by its virtual CPU, struct vcpu in include/xen/sched.h.
The XendDomain class and the XendDomainInfo class play a significant part in creating and destroying domains. The domain_create() method in XendDomain class is called when we create a new domain; it starts the process of creating of a domain.
The XendDomainInfo class and its methods are responsible for the actual construction of a domain. The construction process includes setting up the devices in the new domain. This involves a lot of messaging between the front end device drivers in the domain and the back end device drivers in the back end domain. We talk about the back end and front end device drivers later.
The XCS server opens two TCP sockets, the control connection and the data connection. The difference between the control connection and the data connection is the control connection is synchronous while the data connection is asynchronous. The notifier object, which was mentioned before, for example, is a client of the XCS server.
A connection to the XCS server is represented by an object of type connection_t. After a connection is bound, it is added to a list of connections, connection_list, which is iterated every five seconds to see whether new control or data messages arrived. Control messages, which can be control or data messages, are handled by handle_control_message() or by handle_data_message(), respectively.
The create() method in XendDomainInfo starts a chain of actions to create a domain. The virtual devices of the domain first are created. The create() method calls create_blkif() to create a block device interface (blkif); this is a must even if the VM doesn't use a disk. The other virtual devices are created by create_configured_devices(), which eventually calls the createDevice() method of DevController class (see controller.py). This method calls the newDevice() method of the corresponding class. All the device classes inherit from Dev, which is an abstract class representing a device attached to a device controller. Its attach() abstract (empty) method is implemented in each subclass of the Dev class; this method attaches the device to its front end and back end. Figure 2 shows the devices hierarchy, and Figure 3 shows the device controller hierarchy.
Domain 0 runs the back end drivers, and the newly created domain runs the front end drivers. A lot of messages pass between the back end and front end drivers. The front end driver is a virtual driver in the sense that it does not use specific hardware details; the code resides in drivers/xen, in the sparse tree.
Event channels and shared-memory rings are the means of communication among domains. For example, in the case of netfront device (netfront.c), which is the network card front end interface, the np->tx and the np->rx are the shared memory pages, one for the receiver buffer and one for the transmitted buffer. In send_interface_connect(), we tell the netback end to bring up the interface. The connect message travels through the event channel to the netif_connect() method of the back end, interface.c. The netif_connect() method calls the get_vm_area(2*PAGE_SIZE, VM_IOREMAP)). The get_vm_area() method searches in the kernel virtual mapping area for an area whose size equals two pages.
In the blkif case, which is the block device front end interface, blkif_connect() also calls get_vm_area(). In this case, however, it uses only one page of memory.
The interrupts associated with virtual devices are virtual interrupts. When you run cat /proc/interrupts from domainU, look at the interrupts with numbers higher than 256; they are labeled "Dynamic-irq".
How are IRQs redirected to the guest OS? The do_IRQ() method was changed to support IRQs for the guest OS. This method calls __do_IRQ_guest() if the IRQ is for the guest OS, xen/arch/x86/irq.c. The __do_IRQ_guest() uses the event channel mechanism to send the interrupt to the guest OS, send_guest_pirq() method in event_channel.c.