mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 18:50:05 +01:00
bitkeeper revision 1.1159.212.23 (41ee5e8fnbA6nB8wOl_As73BmvhklQ)
Support for USB virtualisation. Adds the USB frontend / backend, update build options, make the control tools understand USB interfaces. One extra file from vanilla Linux is imported (linux-2.4.28-xen-sparse/drivers/usb/hcd.c) because small changes were required to make XenU domains build with a USB frontend. This will not be required for the revised host controller infrastructure in Linux 2.6. To use this, set "usb = " a list of USB ports in the domain config file. Ports are specified as a path, e.g. '1' = the first USB port on the host, '1/2' is the second port on the hub on the first host port, etc. Should work for most mass storage and isochronous devices. May work for some HID devices but these are not as intelligent and may have trouble supporting virtualisation.
This commit is contained in:
1 parent
f4eadd8b65
commit
1858ac4f9d
25 files changed
+6072
-4
No files matched your search
@@ -562,6 +562,24 @@ static PyTypeObject xu_notifier_type = {
|
||||
PyDict_SetItemString(dict, #_field, obj); \
|
||||
} while ( 0 )
|
||||
|
||||
#define PSTR2CHAR(_struct, _field) \
|
||||
do { \
|
||||
PyObject *obj; \
|
||||
if ( (obj = PyDict_GetItemString(payload, #_field)) != NULL ) \
|
||||
{ \
|
||||
if ( PyString_Check(obj) ) \
|
||||
{ \
|
||||
char *buffer = PyString_AsString(obj); \
|
||||
\
|
||||
strcpy(((_struct *)&xum->msg.msg[0])->_field, \
|
||||
buffer); \
|
||||
/* Should complain about length - think later */ \
|
||||
dict_items_parsed++; \
|
||||
} \
|
||||
} \
|
||||
xum->msg.length = sizeof(_struct); \
|
||||
} while ( 0 )
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD;
|
||||
control_msg_t msg;
|
||||
@@ -753,6 +771,52 @@ static PyObject *xu_message_get_payload(PyObject *self, PyObject *args)
|
||||
case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_DRIVER_STATUS):
|
||||
C2P(netif_be_driver_status_t, status, Int, Long);
|
||||
return dict;
|
||||
case TYPE(CMSG_USBIF_FE, CMSG_USBIF_FE_INTERFACE_STATUS_CHANGED):
|
||||
C2P(usbif_fe_interface_status_changed_t, status, Int, Long);
|
||||
C2P(usbif_fe_interface_status_changed_t, evtchn, Int, Long);
|
||||
C2P(usbif_fe_interface_status_changed_t, domid, Int, Long);
|
||||
C2P(usbif_fe_interface_status_changed_t, bandwidth, Int, Long);
|
||||
C2P(usbif_fe_interface_status_changed_t, num_ports, Int, Long);
|
||||
return dict;
|
||||
case TYPE(CMSG_USBIF_FE, CMSG_USBIF_FE_DRIVER_STATUS_CHANGED):
|
||||
C2P(usbif_fe_driver_status_changed_t, status, Int, Long);
|
||||
return dict;
|
||||
case TYPE(CMSG_USBIF_FE, CMSG_USBIF_FE_INTERFACE_CONNECT):
|
||||
C2P(usbif_fe_interface_connect_t, shmem_frame, Int, Long);
|
||||
return dict;
|
||||
case TYPE(CMSG_USBIF_FE, CMSG_USBIF_FE_INTERFACE_DISCONNECT):
|
||||
return dict;
|
||||
case TYPE(CMSG_USBIF_BE, CMSG_USBIF_BE_CREATE):
|
||||
C2P(usbif_be_create_t, domid, Int, Long);
|
||||
C2P(usbif_be_create_t, status, Int, Long);
|
||||
return dict;
|
||||
case TYPE(CMSG_USBIF_BE, CMSG_USBIF_BE_DESTROY):
|
||||
C2P(usbif_be_destroy_t, domid, Int, Long);
|
||||
C2P(usbif_be_destroy_t, status, Int, Long);
|
||||
return dict;
|
||||
case TYPE(CMSG_USBIF_BE, CMSG_USBIF_BE_CONNECT):
|
||||
C2P(usbif_be_connect_t, domid, Int, Long);
|
||||
C2P(usbif_be_connect_t, shmem_frame, Int, Long);
|
||||
C2P(usbif_be_connect_t, evtchn, Int, Long);
|
||||
C2P(usbif_be_connect_t, bandwidth, Int, Long);
|
||||
C2P(usbif_be_connect_t, status, Int, Long);
|
||||
return dict;
|
||||
case TYPE(CMSG_USBIF_BE, CMSG_USBIF_BE_DISCONNECT):
|
||||
C2P(usbif_be_disconnect_t, domid, Int, Long);
|
||||
C2P(usbif_be_disconnect_t, status, Int, Long);
|
||||
return dict;
|
||||
case TYPE(CMSG_USBIF_BE, CMSG_USBIF_BE_DRIVER_STATUS_CHANGED):
|
||||
C2P(usbif_be_driver_status_changed_t, status, Int, Long);
|
||||
return dict;
|
||||
case TYPE(CMSG_USBIF_BE, CMSG_USBIF_BE_CLAIM_PORT):
|
||||
C2P(usbif_be_claim_port_t, domid, Int, Long);
|
||||
C2P(usbif_be_claim_port_t, usbif_port, Int, Long);
|
||||
C2P(usbif_be_claim_port_t, status, Int, Long);
|
||||
C2P(usbif_be_claim_port_t, path, String, String);
|
||||
return dict;
|
||||
case TYPE(CMSG_USBIF_BE, CMSG_USBIF_BE_RELEASE_PORT):
|
||||
C2P(usbif_be_release_port_t, path, String, String);
|
||||
return dict;
|
||||
case TYPE(CMSG_MEM_REQUEST, CMSG_MEM_REQUEST_SET):
|
||||
C2P(mem_request_t, target, Int, Long);
|
||||
C2P(mem_request_t, status, Int, Long);
|
||||
@@ -922,6 +986,53 @@ static PyObject *xu_message_new(PyObject *self, PyObject *args)
|
||||
P2C(mem_request_t, target, u32);
|
||||
P2C(mem_request_t, status, u32);
|
||||
break;
|
||||
case TYPE(CMSG_USBIF_FE, CMSG_USBIF_FE_INTERFACE_STATUS_CHANGED):
|
||||
P2C(usbif_fe_interface_status_changed_t, status, u32);
|
||||
P2C(usbif_fe_interface_status_changed_t, evtchn, u16);
|
||||
P2C(usbif_fe_interface_status_changed_t, domid, domid_t);
|
||||
P2C(usbif_fe_interface_status_changed_t, bandwidth, u32);
|
||||
P2C(usbif_fe_interface_status_changed_t, num_ports, u32);
|
||||
break;
|
||||
case TYPE(CMSG_USBIF_FE, CMSG_USBIF_FE_DRIVER_STATUS_CHANGED):
|
||||
P2C(usbif_fe_driver_status_changed_t, status, u32);
|
||||
break;
|
||||
case TYPE(CMSG_USBIF_FE, CMSG_USBIF_FE_INTERFACE_CONNECT):
|
||||
P2C(usbif_fe_interface_connect_t, shmem_frame, memory_t);
|
||||
break;
|
||||
case TYPE(CMSG_USBIF_FE, CMSG_USBIF_FE_INTERFACE_DISCONNECT):
|
||||
break;
|
||||
case TYPE(CMSG_USBIF_BE, CMSG_USBIF_BE_CREATE):
|
||||
P2C(usbif_be_create_t, domid, domid_t);
|
||||
P2C(usbif_be_create_t, status, u32);
|
||||
break;
|
||||
case TYPE(CMSG_USBIF_BE, CMSG_USBIF_BE_DESTROY):
|
||||
P2C(usbif_be_destroy_t, domid, domid_t);
|
||||
P2C(usbif_be_destroy_t, status, u32);
|
||||
break;
|
||||
case TYPE(CMSG_USBIF_BE, CMSG_USBIF_BE_CONNECT):
|
||||
P2C(usbif_be_connect_t, domid, domid_t);
|
||||
P2C(usbif_be_connect_t, shmem_frame, memory_t);
|
||||
P2C(usbif_be_connect_t, evtchn, u32);
|
||||
P2C(usbif_be_connect_t, bandwidth, u32);
|
||||
P2C(usbif_be_connect_t, status, u32);
|
||||
break;
|
||||
case TYPE(CMSG_USBIF_BE, CMSG_USBIF_BE_DISCONNECT):
|
||||
P2C(usbif_be_disconnect_t, domid, domid_t);
|
||||
P2C(usbif_be_disconnect_t, status, u32);
|
||||
break;
|
||||
case TYPE(CMSG_USBIF_BE, CMSG_USBIF_BE_DRIVER_STATUS_CHANGED):
|
||||
P2C(usbif_be_driver_status_changed_t, status, u32);
|
||||
break;
|
||||
case TYPE(CMSG_USBIF_BE, CMSG_USBIF_BE_CLAIM_PORT):
|
||||
P2C(usbif_be_claim_port_t, domid, domid_t);
|
||||
P2C(usbif_be_claim_port_t, usbif_port, u32);
|
||||
P2C(usbif_be_claim_port_t, status, u32);
|
||||
PSTR2CHAR(usbif_be_claim_port_t, path);
|
||||
printf("dict items parsed = %d", dict_items_parsed);
|
||||
break;
|
||||
case TYPE(CMSG_USBIF_BE, CMSG_USBIF_BE_RELEASE_PORT):
|
||||
PSTR2CHAR(usbif_be_release_port_t, path);
|
||||
break;
|
||||
}
|
||||
|
||||
if ( dict_items_parsed != PyDict_Size(payload) )
|
||||
|
||||
@@ -658,6 +658,7 @@ class XendDomainInfo:
|
||||
"""
|
||||
self.release_vifs()
|
||||
self.release_vbds()
|
||||
self.release_usbifs()
|
||||
|
||||
self.devices = {}
|
||||
self.device_index = {}
|
||||
@@ -682,6 +683,15 @@ class XendDomainInfo:
|
||||
log.debug("Destroying vbds for domain %d", self.dom)
|
||||
ctrl.destroy()
|
||||
|
||||
def release_usbifs(self):
|
||||
"""Release vm virtual USB devices (usbifs).
|
||||
"""
|
||||
if self.dom is None: return
|
||||
ctrl = xend.usbif_get(self.dom)
|
||||
if ctrl:
|
||||
log.debug("Destroying usbifs for domain %d", self.dom)
|
||||
ctrl.destroy()
|
||||
|
||||
def show(self):
|
||||
"""Print virtual machine info.
|
||||
"""
|
||||
@@ -994,6 +1004,8 @@ class XendDomainInfo:
|
||||
self.blkif_backend = 1
|
||||
elif name == 'netif':
|
||||
self.netif_backend = 1
|
||||
elif name == 'usbif':
|
||||
self.usbif_backend = 1
|
||||
else:
|
||||
raise VmError('invalid backend type:' + str(name))
|
||||
|
||||
@@ -1174,6 +1186,23 @@ def vm_dev_vif(vm, val, index, change=0):
|
||||
defer.addCallback(cbok)
|
||||
return defer
|
||||
|
||||
def vm_dev_usb(vm, val, index):
|
||||
"""Attach the relevant physical ports to the domains' USB interface.
|
||||
|
||||
@param vm: virtual machine
|
||||
@param val: USB interface config
|
||||
@param index: USB interface index
|
||||
@return: deferred
|
||||
"""
|
||||
ctrl = xend.usbif_create(vm.dom, recreate=vm.recreate)
|
||||
log.debug("Creating USB interface dom=%d", vm.dom)
|
||||
defer = ctrl.attachDevice(val, recreate=vm.recreate)
|
||||
def cbok(path):
|
||||
vm.add_device('usb', val[1][1])
|
||||
return path
|
||||
defer.addCallback(cbok)
|
||||
return defer
|
||||
|
||||
def vm_dev_vbd(vm, val, index, change=0):
|
||||
"""Create a virtual block device (vbd).
|
||||
|
||||
@@ -1278,6 +1307,7 @@ add_image_handler('vmx', vm_image_vmx)
|
||||
add_device_handler('vif', vm_dev_vif)
|
||||
add_device_handler('vbd', vm_dev_vbd)
|
||||
add_device_handler('pci', vm_dev_pci)
|
||||
add_device_handler('usb', vm_dev_usb)
|
||||
|
||||
# Ignore the fields we already handle.
|
||||
add_config_handler('name', vm_field_ignore)
|
||||
|
||||
@@ -40,6 +40,7 @@ from xen.util.ip import _readline, _readlines
|
||||
import channel
|
||||
import blkif
|
||||
import netif
|
||||
import usbif
|
||||
import console
|
||||
import domain
|
||||
from params import *
|
||||
@@ -261,6 +262,7 @@ class EventProtocol(protocol.Protocol):
|
||||
val += self.daemon.consoles()
|
||||
val += self.daemon.blkifs()
|
||||
val += self.daemon.netifs()
|
||||
val += self.daemon.usbifs()
|
||||
return val
|
||||
|
||||
def op_sys_subscribe(self, name, v):
|
||||
@@ -617,6 +619,7 @@ class Daemon:
|
||||
self.domainCF = domain.DomainControllerFactory()
|
||||
self.blkifCF = blkif.BlkifControllerFactory()
|
||||
self.netifCF = netif.NetifControllerFactory()
|
||||
self.usbifCF = usbif.UsbifControllerFactory()
|
||||
self.consoleCF = console.ConsoleControllerFactory()
|
||||
|
||||
def listenEvent(self):
|
||||
@@ -683,6 +686,15 @@ class Daemon:
|
||||
def netif_get(self, dom):
|
||||
return self.netifCF.getControllerByDom(dom)
|
||||
|
||||
def usbif_create(self, dom, recreate=0):
|
||||
return self.usbifCF.getController(dom)
|
||||
|
||||
def usbifs(self):
|
||||
return [ x.sxpr() for x in self.usbifCF.getControllers() ]
|
||||
|
||||
def usbif_get(self, dom):
|
||||
return self.usbifCF.getControllerByDom(dom)
|
||||
|
||||
def console_create(self, dom, console_port=None):
|
||||
"""Create a console for a domain.
|
||||
"""
|
||||
|
||||
@@ -0,0 +1,249 @@
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
|
||||
from twisted.protocols import http
|
||||
|
||||
from xen.xend import sxp
|
||||
from xen.xend import XendDomain
|
||||
from xen.xend import XendConsole
|
||||
from xen.xend import PrettyPrint
|
||||
from xen.xend.Args import FormFn
|
||||
|
||||
from SrvDir import SrvDir
|
||||
|
||||
class SrvDomain(SrvDir):
|
||||
"""Service managing a single domain.
|
||||
"""
|
||||
|
||||
def __init__(self, dom):
|
||||
SrvDir.__init__(self)
|
||||
self.dom = dom
|
||||
self.xd = XendDomain.instance()
|
||||
self.xconsole = XendConsole.instance()
|
||||
|
||||
def op_configure(self, op, req):
|
||||
"""Configure an existing domain.
|
||||
Configure is unusual in that it requires a domain id,
|
||||
not a domain name.
|
||||
"""
|
||||
fn = FormFn(self.xd.domain_configure,
|
||||
[['dom', 'int'],
|
||||
['config', 'sxpr']])
|
||||
deferred = fn(req.args, {'dom': self.dom.dom})
|
||||
deferred.addErrback(self._op_configure_err, req)
|
||||
return deferred
|
||||
|
||||
def _op_configure_err(self, err, req):
|
||||
req.setResponseCode(http.BAD_REQUEST, "Error: "+ str(err))
|
||||
return str(err)
|
||||
|
||||
def op_unpause(self, op, req):
|
||||
val = self.xd.domain_unpause(self.dom.name)
|
||||
return val
|
||||
|
||||
def op_pause(self, op, req):
|
||||
val = self.xd.domain_pause(self.dom.name)
|
||||
return val
|
||||
|
||||
def op_shutdown(self, op, req):
|
||||
fn = FormFn(self.xd.domain_shutdown,
|
||||
[['dom', 'str'],
|
||||
['reason', 'str']])
|
||||
val = fn(req.args, {'dom': self.dom.id})
|
||||
req.setResponseCode(http.ACCEPTED)
|
||||
req.setHeader("Location", "%s/.." % req.prePathURL())
|
||||
return val
|
||||
|
||||
def op_destroy(self, op, req):
|
||||
fn = FormFn(self.xd.domain_destroy,
|
||||
[['dom', 'str'],
|
||||
['reason', 'str']])
|
||||
val = fn(req.args, {'dom': self.dom.id})
|
||||
req.setHeader("Location", "%s/.." % req.prePathURL())
|
||||
return val
|
||||
|
||||
def op_save(self, op, req):
|
||||
fn = FormFn(self.xd.domain_save,
|
||||
[['dom', 'str'],
|
||||
['file', 'str']])
|
||||
deferred = fn(req.args, {'dom': self.dom.id})
|
||||
deferred.addCallback(self._op_save_cb, req)
|
||||
deferred.addErrback(self._op_save_err, req)
|
||||
return deferred
|
||||
|
||||
def _op_save_cb(self, val, req):
|
||||
return 0
|
||||
|
||||
def _op_save_err(self, err, req):
|
||||
req.setResponseCode(http.BAD_REQUEST, "Error: "+ str(err))
|
||||
return str(err)
|
||||
|
||||
def op_migrate(self, op, req):
|
||||
fn = FormFn(self.xd.domain_migrate,
|
||||
[['dom', 'str'],
|
||||
['destination', 'str'],
|
||||
['live', 'int']])
|
||||
deferred = fn(req.args, {'dom': self.dom.id})
|
||||
print 'op_migrate>', deferred
|
||||
deferred.addCallback(self._op_migrate_cb, req)
|
||||
deferred.addErrback(self._op_migrate_err, req)
|
||||
return deferred
|
||||
|
||||
def _op_migrate_cb(self, info, req):
|
||||
print '_op_migrate_cb>', info, req
|
||||
#req.setResponseCode(http.ACCEPTED)
|
||||
host = info.dst_host
|
||||
port = info.dst_port
|
||||
dom = info.dst_dom
|
||||
url = "http://%s:%d/xend/domain/%d" % (host, port, dom)
|
||||
req.setHeader("Location", url)
|
||||
print '_op_migrate_cb> url=', url
|
||||
return url
|
||||
|
||||
def _op_migrate_err(self, err, req):
|
||||
print '_op_migrate_err>', err, req
|
||||
req.setResponseCode(http.BAD_REQUEST, "Error: "+ str(err))
|
||||
return str(err)
|
||||
|
||||
def op_pincpu(self, op, req):
|
||||
fn = FormFn(self.xd.domain_pincpu,
|
||||
[['dom', 'str'],
|
||||
['cpu', 'int']])
|
||||
val = fn(req.args, {'dom': self.dom.id})
|
||||
return val
|
||||
|
||||
def op_cpu_bvt_set(self, op, req):
|
||||
fn = FormFn(self.xd.domain_cpu_bvt_set,
|
||||
[['dom', 'str'],
|
||||
['mcuadv', 'int'],
|
||||
['warpback', 'int'],
|
||||
['warpvalue', 'int'],
|
||||
['warpl', 'long'],
|
||||
['warpu', 'long']])
|
||||
val = fn(req.args, {'dom': self.dom.id})
|
||||
return val
|
||||
|
||||
def op_cpu_fbvt_set(self, op, req):
|
||||
fn = FormFn(self.xd.domain_cpu_fbvt_set,
|
||||
[['dom', 'str'],
|
||||
['mcuadv', 'int'],
|
||||
['warp', 'int'],
|
||||
['warpl', 'int'],
|
||||
['warpu', 'int']])
|
||||
val = fn(req.args, {'dom': self.dom.id})
|
||||
return val
|
||||
|
||||
def op_cpu_atropos_set(self, op, req):
|
||||
fn = FormFn(self.xd.domain_cpu_atropos_set,
|
||||
[['dom', 'str'],
|
||||
['period', 'int'],
|
||||
['slice', 'int'],
|
||||
['latency', 'int'],
|
||||
['xtratime', 'int']])
|
||||
val = fn(req.args, {'dom': self.dom.id})
|
||||
return val
|
||||
|
||||
def op_maxmem_set(self, op, req):
|
||||
fn = FormFn(self.xd.domain_maxmem_set,
|
||||
[['dom', 'str'],
|
||||
['memory', 'int']])
|
||||
val = fn(req.args, {'dom': self.dom.id})
|
||||
return val
|
||||
|
||||
def op_device_create(self, op, req):
|
||||
fn = FormFn(self.xd.domain_device_create,
|
||||
[['dom', 'str'],
|
||||
['config', 'sxpr']])
|
||||
d = fn(req.args, {'dom': self.dom.id})
|
||||
return d
|
||||
|
||||
def op_device_destroy(self, op, req):
|
||||
fn = FormFn(self.xd.domain_device_destroy,
|
||||
[['dom', 'str'],
|
||||
['type', 'str'],
|
||||
['idx', 'str']])
|
||||
val = fn(req.args, {'dom': self.dom.id})
|
||||
return val
|
||||
|
||||
def op_vifs(self, op, req):
|
||||
devs = self.xd.domain_vif_ls(self.dom.id)
|
||||
return [ dev.sxpr() for dev in devs ]
|
||||
|
||||
def op_vif(self, op, req):
|
||||
fn = FormFn(self.xd.domain_vif_get,
|
||||
[['dom', 'str'],
|
||||
['vif', 'str']])
|
||||
val = fn(req.args, {'dom': self.dom.id})
|
||||
return val
|
||||
|
||||
def op_vbds(self, op, req):
|
||||
devs = self.xd.domain_vbd_ls(self.dom.id)
|
||||
return [ dev.sxpr() for dev in devs ]
|
||||
|
||||
def op_vbd(self, op, req):
|
||||
fn = FormFn(self.xd.domain_vbd_get,
|
||||
[['dom', 'str'],
|
||||
['vbd', 'str']])
|
||||
val = fn(req.args, {'dom': self.dom.id})
|
||||
return val
|
||||
|
||||
def render_POST(self, req):
|
||||
return self.perform(req)
|
||||
|
||||
def render_GET(self, req):
|
||||
op = req.args.get('op')
|
||||
if op and op[0] in ['vifs', 'vif', 'vbds', 'vbd']:
|
||||
return self.perform(req)
|
||||
if self.use_sxp(req):
|
||||
req.setHeader("Content-Type", sxp.mime_type)
|
||||
sxp.show(self.dom.sxpr(), out=req)
|
||||
else:
|
||||
req.write('<html><head></head><body>')
|
||||
self.print_path(req)
|
||||
#self.ls()
|
||||
req.write('<p>%s</p>' % self.dom)
|
||||
if self.dom.console:
|
||||
cinfo = self.dom.console
|
||||
cid = str(cinfo.console_port)
|
||||
#todo: Local xref: need to know server prefix.
|
||||
req.write('<p><a href="/xend/console/%s">Console %s</a></p>'
|
||||
% (cid, cid))
|
||||
req.write('<p><a href="%s">Connect to console</a></p>'
|
||||
% cinfo.uri())
|
||||
if self.dom.config:
|
||||
req.write("<code><pre>")
|
||||
PrettyPrint.prettyprint(self.dom.config, out=req)
|
||||
req.write("</pre></code>")
|
||||
self.form(req)
|
||||
req.write('</body></html>')
|
||||
return ''
|
||||
|
||||
def form(self, req):
|
||||
url = req.prePathURL()
|
||||
req.write('<form method="post" action="%s">' % url)
|
||||
req.write('<input type="submit" name="op" value="unpause">')
|
||||
req.write('<input type="submit" name="op" value="pause">')
|
||||
req.write('</form>')
|
||||
|
||||
req.write('<form method="post" action="%s">' % url)
|
||||
req.write('<input type="submit" name="op" value="destroy">')
|
||||
req.write('<input type="radio" name="reason" value="halt" checked>Halt')
|
||||
req.write('<input type="radio" name="reason" value="reboot">Reboot')
|
||||
req.write('</form>')
|
||||
|
||||
req.write('<form method="post" action="%s">' % url)
|
||||
req.write('<input type="submit" name="op" value="shutdown">')
|
||||
req.write('<input type="radio" name="reason" value="poweroff" checked>Poweroff')
|
||||
req.write('<input type="radio" name="reason" value="halt">Halt')
|
||||
req.write('<input type="radio" name="reason" value="reboot">Reboot')
|
||||
req.write('</form>')
|
||||
|
||||
req.write('<form method="post" action="%s">' % url)
|
||||
req.write('<br><input type="submit" name="op" value="save">')
|
||||
req.write(' To file: <input type="text" name="file">')
|
||||
req.write('</form>')
|
||||
|
||||
req.write('<form method="post" action="%s">' % url)
|
||||
req.write('<br><input type="submit" name="op" value="migrate">')
|
||||
req.write(' To host: <input type="text" name="destination">')
|
||||
req.write('<input type="checkbox" name="live" value="1">Live')
|
||||
req.write('</form>')
|
||||
@@ -188,6 +188,70 @@ netif_formats = {
|
||||
|
||||
msg_formats.update(netif_formats)
|
||||
|
||||
#============================================================================
|
||||
# USB interface message types.
|
||||
#============================================================================
|
||||
|
||||
CMSG_USBIF_BE = 8
|
||||
CMSG_USBIF_FE = 9
|
||||
|
||||
CMSG_USBIF_FE_INTERFACE_STATUS_CHANGED = 0
|
||||
|
||||
CMSG_USBIF_FE_DRIVER_STATUS_CHANGED = 32
|
||||
CMSG_USBIF_FE_INTERFACE_CONNECT = 33
|
||||
CMSG_USBIF_FE_INTERFACE_DISCONNECT = 34
|
||||
|
||||
USBIF_DRIVER_STATUS_DOWN = 0
|
||||
USBIF_DRIVER_STATUS_UP = 1
|
||||
|
||||
USBIF_INTERFACE_STATUS_DESTROYED = 0 #/* Interface doesn't exist. */
|
||||
USBIF_INTERFACE_STATUS_DISCONNECTED = 1 #/* Exists but is disconnected. */
|
||||
USBIF_INTERFACE_STATUS_CONNECTED = 2 #/* Exists and is connected. */
|
||||
|
||||
CMSG_USBIF_BE_CREATE = 0
|
||||
CMSG_USBIF_BE_DESTROY = 1
|
||||
CMSG_USBIF_BE_CONNECT = 2
|
||||
|
||||
CMSG_USBIF_BE_DISCONNECT = 3
|
||||
CMSG_USBIF_BE_CLAIM_PORT = 4
|
||||
CMSG_USBIF_BE_RELEASE_PORT = 5
|
||||
|
||||
CMSG_USBIF_BE_DRIVER_STATUS_CHANGED = 32
|
||||
|
||||
USBIF_BE_STATUS_OKAY = 0
|
||||
USBIF_BE_STATUS_ERROR = 1
|
||||
|
||||
USBIF_BE_STATUS_INTERFACE_EXISTS = 2
|
||||
USBIF_BE_STATUS_INTERFACE_NOT_FOUND = 3
|
||||
USBIF_BE_STATUS_INTERFACE_CONNECTED = 4
|
||||
USBIF_BE_STATUS_OUT_OF_MEMORY = 7
|
||||
USBIF_BE_STATUS_MAPPING_ERROR = 9
|
||||
|
||||
usbif_formats = {
|
||||
'usbif_be_create_t':
|
||||
(CMSG_USBIF_BE, CMSG_USBIF_BE_CREATE),
|
||||
'usbif_be_destroy_t':
|
||||
(CMSG_USBIF_BE, CMSG_USBIF_BE_DESTROY),
|
||||
'usbif_be_connect_t':
|
||||
(CMSG_USBIF_BE, CMSG_USBIF_BE_CONNECT),
|
||||
'usbif_be_disconnect_t':
|
||||
(CMSG_USBIF_BE, CMSG_USBIF_BE_DISCONNECT),
|
||||
'usbif_be_claim_port_t':
|
||||
(CMSG_USBIF_BE, CMSG_USBIF_BE_CLAIM_PORT),
|
||||
'usbif_be_release_port_t':
|
||||
(CMSG_USBIF_BE, CMSG_USBIF_BE_RELEASE_PORT),
|
||||
'usbif_fe_interface_status_changed_t':
|
||||
(CMSG_USBIF_FE, CMSG_USBIF_FE_INTERFACE_STATUS_CHANGED),
|
||||
'usbif_fe_driver_status_changed_t':
|
||||
(CMSG_USBIF_FE, CMSG_USBIF_FE_DRIVER_STATUS_CHANGED),
|
||||
'usbif_fe_interface_connect_t':
|
||||
(CMSG_USBIF_FE, CMSG_USBIF_FE_INTERFACE_CONNECT),
|
||||
'usbif_fe_interface_disconnect_t':
|
||||
(CMSG_USBIF_FE, CMSG_USBIF_FE_INTERFACE_DISCONNECT)
|
||||
}
|
||||
|
||||
msg_formats.update(usbif_formats)
|
||||
|
||||
#============================================================================
|
||||
# Domain shutdown message types.
|
||||
#============================================================================
|
||||
|
||||
@@ -0,0 +1,368 @@
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
# Copyright (C) 2004 Intel Research Cambridge
|
||||
# Copyright (C) 2004 Mark Williamson <mark.williamson@cl.cam.ac.uk>
|
||||
"""Support for virtual USB hubs.
|
||||
"""
|
||||
|
||||
from twisted.internet import defer
|
||||
#defer.Deferred.debug = 1
|
||||
|
||||
from xen.xend import sxp
|
||||
from xen.xend.XendLogging import log
|
||||
from xen.xend.XendError import XendError
|
||||
|
||||
import channel
|
||||
import controller
|
||||
from messages import *
|
||||
|
||||
class UsbifBackendController(controller.BackendController):
|
||||
""" Handler for the 'back-end' channel to a USB hub domain.
|
||||
Must be connected using connect() before it can be used.
|
||||
Do not create directly - use getBackend() on the UsbifController.
|
||||
"""
|
||||
|
||||
def __init__(self, ctrl, dom):
|
||||
controller.BackendController.__init__(self, ctrl, dom)
|
||||
self.connected = 0
|
||||
self.evtchn = None
|
||||
self.addMethod(CMSG_USBIF_BE,
|
||||
CMSG_USBIF_BE_DRIVER_STATUS_CHANGED,
|
||||
self.recv_be_driver_status_changed)
|
||||
self.registerChannel()
|
||||
|
||||
def __str__(self):
|
||||
return '<UsbifBackendController %d>' % (self.dom)
|
||||
|
||||
def recv_be_driver_status_changed(self, msg, req):
|
||||
"""Request handler for be_driver_status_changed messages.
|
||||
|
||||
@param msg: message
|
||||
@type msg: xu message
|
||||
@param req: request flag (true if the msg is a request)
|
||||
@type req: bool
|
||||
"""
|
||||
val = unpackMsg('usbif_be_driver_status_changed_t', msg)
|
||||
status = val['status']
|
||||
|
||||
class UsbifBackendInterface(controller.BackendInterface):
|
||||
"""Handler for the 'back-end' channel to a network device driver domain
|
||||
on behalf of a front-end domain.
|
||||
|
||||
Each network device is handled separately, so we add no functionality
|
||||
here.
|
||||
"""
|
||||
def __init__(self, ctrl, dom):
|
||||
controller.BackendInterface.__init__(self, ctrl, dom, 0)
|
||||
self.connected = 0
|
||||
self.connecting = False
|
||||
|
||||
def connect(self, recreate=0):
|
||||
"""Connect the controller to the usbif control interface.
|
||||
|
||||
@param recreate: true if after xend restart
|
||||
@return: deferred
|
||||
"""
|
||||
log.debug("Connecting usbif %s", str(self))
|
||||
if recreate or self.connected or self.connecting:
|
||||
d = defer.succeed(self)
|
||||
else:
|
||||
self.connecting = True
|
||||
d = self.send_be_create()
|
||||
d.addCallback(self.respond_be_create)
|
||||
return d
|
||||
|
||||
def send_be_create(self):
|
||||
d = defer.Deferred()
|
||||
msg = packMsg('usbif_be_create_t',
|
||||
{ 'domid' : self.controller.dom })
|
||||
self.writeRequest(msg, response=d)
|
||||
return d
|
||||
|
||||
def respond_be_create(self, msg):
|
||||
val = unpackMsg('usbif_be_create_t', msg)
|
||||
log.debug('>UsbifBackendController>respond_be_create> %s', str(val))
|
||||
self.connected = True
|
||||
return self
|
||||
|
||||
def destroy(self):
|
||||
"""Disconnect from the usbif control interface and destroy it.
|
||||
"""
|
||||
def cb_destroy(val):
|
||||
self.send_be_destroy()
|
||||
d = defer.Deferred()
|
||||
d.addCallback(cb_destroy)
|
||||
self.send_be_disconnect(response=d)
|
||||
|
||||
def send_be_disconnect(self, response=None):
|
||||
log.debug('>UsbifBackendController>send_be_disconnect> %s', str(self))
|
||||
msg = packMsg('usbif_be_disconnect_t',
|
||||
{ 'domid' : self.controller.dom })
|
||||
self.writeRequest(msg, response=response)
|
||||
|
||||
def send_be_destroy(self, response=None):
|
||||
log.debug('>UsbifBackendController>send_be_destroy> %s', str(self))
|
||||
msg = packMsg('usbif_be_destroy_t',
|
||||
{ 'domid' : self.controller.dom })
|
||||
self.writeRequest(msg, response=response)
|
||||
|
||||
def send_be_claim_port(self, path):
|
||||
d=defer.Deferred()
|
||||
log.debug(">UsbifBackendController>send_be_claim_port> about to claim port %s" % path)
|
||||
def cb(blah): log.debug(">UsbifBackendController> Claim port completed")
|
||||
d.addCallback(cb)
|
||||
msg = packMsg('usbif_be_claim_port_t',
|
||||
{ 'domid' : self.controller.dom,
|
||||
'path' : path,
|
||||
'usbif_port' : self.controller.devices[path],
|
||||
'status' : 0})
|
||||
self.writeRequest(msg, response=d)
|
||||
# No need to add any callbacks, since the guest polls its virtual ports
|
||||
# anyhow, somewhat like a UHCI controller ;-)
|
||||
return d
|
||||
|
||||
def send_be_release_port(self, path):
|
||||
d=defer.Deferred()
|
||||
def cb(blah): log.debug(">UsbifBackendController> Release port completed")
|
||||
d.addCallback(cb)
|
||||
msg = packMsg('usbif_be_release_port_t',
|
||||
{ 'domid' : self.controller.dom,
|
||||
'path' : path })
|
||||
self.writeRequest(msg, response)
|
||||
# No need to add any callbacks, since the guest polls its virtual ports
|
||||
# anyhow, somewhat like a UHCI controller ;-)
|
||||
|
||||
def connectInterface(self, val):
|
||||
self.evtchn = channel.eventChannel(0, self.controller.dom)
|
||||
log.debug(">UsbifBackendController>connectInterface> connecting usbif to event channel %s ports=%d:%d",
|
||||
str(self), self.evtchn['port1'], self.evtchn['port2'])
|
||||
msg = packMsg('usbif_be_connect_t',
|
||||
{ 'domid' : self.controller.dom,
|
||||
'evtchn' : self.evtchn['port1'],
|
||||
'shmem_frame' : val['shmem_frame'],
|
||||
'bandwidth' : 500 # XXX fix bandwidth!
|
||||
})
|
||||
d = defer.Deferred()
|
||||
d.addCallback(self.respond_be_connect)
|
||||
self.writeRequest(msg, response=d)
|
||||
|
||||
def respond_be_connect(self, msg):
|
||||
"""Response handler for a be_connect message.
|
||||
|
||||
@param msg: message
|
||||
@type msg: xu message
|
||||
"""
|
||||
val = unpackMsg('usbif_be_connect_t', msg)
|
||||
log.debug('>UsbifBackendController>respond_be_connect> %s, %s', str(self), str(val))
|
||||
d = defer.Deferred()
|
||||
def cb(blah):
|
||||
log.debug(">UsbifBackendController> Successfully connected USB interface for domain %d" % self.controller.dom)
|
||||
self.controller.claim_ports()
|
||||
d.addCallback(cb)
|
||||
self.send_fe_interface_status_changed(d)
|
||||
|
||||
def send_fe_interface_status_changed(self, response=None):
|
||||
msg = packMsg('usbif_fe_interface_status_changed_t',
|
||||
{ 'status' : USBIF_INTERFACE_STATUS_CONNECTED,
|
||||
'domid' : 0, ## FIXME: should be domid of backend
|
||||
'evtchn' : self.evtchn['port2'],
|
||||
'bandwidth' : 500,
|
||||
'num_ports' : len(self.controller.devices.keys())})
|
||||
self.controller.writeRequest(msg, response=response)
|
||||
|
||||
|
||||
class UsbifControllerFactory(controller.SplitControllerFactory):
|
||||
"""Factory for creating USB interface controllers.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
controller.ControllerFactory.__init__(self)
|
||||
self.backendControllers = {}
|
||||
|
||||
def createController(self, dom, recreate=0):
|
||||
"""Create a USB device controller for a domain.
|
||||
|
||||
@param dom: domain
|
||||
@type dom: int
|
||||
@param recreate: if true it's a recreate (after xend restart)
|
||||
@type recreate: bool
|
||||
@return: block device controller
|
||||
@rtype: UsbifController
|
||||
"""
|
||||
usbif = self.getControllerByDom(dom)
|
||||
if usbif is None:
|
||||
usbif = UsbifController(self, dom)
|
||||
self.addController(usbif)
|
||||
return usbif
|
||||
|
||||
def getDomainDevices(self, dom):
|
||||
"""Get the block devices for a domain.
|
||||
|
||||
@param dom: domain
|
||||
@type dom: int
|
||||
@return: devices
|
||||
@rtype: [device]
|
||||
"""
|
||||
usbif = self.getControllerByDom(dom)
|
||||
return (usbif and usbif.getDevices()) or []
|
||||
|
||||
def getDomainDevice(self, dom, vdev):
|
||||
"""Get a block device from a domain.
|
||||
|
||||
@param dom: domain
|
||||
@type dom: int
|
||||
@param vdev: device index
|
||||
@type vdev: int
|
||||
@return: device
|
||||
@rtype: device
|
||||
"""
|
||||
usbif = self.getControllerByDom(dom)
|
||||
return (usbif and usbif.getDevice(vdev)) or None
|
||||
|
||||
def createBackendInterface(self, ctrl, dom, handle):
|
||||
"""Create a network device backend interface.
|
||||
|
||||
@param ctrl: controller
|
||||
@param dom: backend domain
|
||||
@param handle: interface handle
|
||||
@return: backend interface
|
||||
"""
|
||||
return UsbifBackendInterface(ctrl, dom)
|
||||
|
||||
def getBackendController(self, dom):
|
||||
"""Get the backend controller for a domain, creating
|
||||
if necessary.
|
||||
|
||||
@param dom: backend domain
|
||||
@return: backend controller
|
||||
"""
|
||||
b = self.getBackendControllerByDomain(dom)
|
||||
if b is None:
|
||||
b = self.createBackendController(dom)
|
||||
self.backendControllers[b.dom] = b
|
||||
return b
|
||||
|
||||
def createBackendController(self, dom):
|
||||
return UsbifBackendController(self, dom)
|
||||
|
||||
class UsbifController(controller.SplitController):
|
||||
"""USB device interface controller. Handles all USB devices
|
||||
for a domain.
|
||||
"""
|
||||
|
||||
def __init__(self, factory, dom):
|
||||
"""Create a USB device controller.
|
||||
Do not call directly - use createController() on the factory instead.
|
||||
"""
|
||||
controller.SplitController.__init__(self, factory, dom)
|
||||
self.num_ports = 0
|
||||
self.devices = {}
|
||||
self.addMethod(CMSG_USBIF_FE,
|
||||
CMSG_USBIF_FE_DRIVER_STATUS_CHANGED,
|
||||
self.recv_fe_driver_status_changed)
|
||||
self.addMethod(CMSG_USBIF_FE,
|
||||
CMSG_USBIF_FE_INTERFACE_CONNECT,
|
||||
self.recv_fe_interface_connect)
|
||||
self.registerChannel()
|
||||
try:
|
||||
self.backendDomain = 0 #int(sxp.child_value(config, 'backend', '0')) TODO: configurable backends
|
||||
except:
|
||||
raise XendError('invalid backend domain')
|
||||
|
||||
|
||||
def sxpr(self):
|
||||
val = ['usbif', ['dom', self.dom]]
|
||||
return val
|
||||
|
||||
def createBackend(self, dom, handle):
|
||||
return UsbifBackendController(self, dom, handle)
|
||||
|
||||
def getDevices(self):
|
||||
return self.devices.values()
|
||||
|
||||
def attachDevice(self, path, recreate=0):
|
||||
"""Add privileges for a particular device to the domain.
|
||||
@param path: the Linux-style path to the device port
|
||||
"""
|
||||
self.devices[path[1][1]] = self.num_ports
|
||||
self.num_ports += 1
|
||||
log.debug(">UsbifController>attachDevice> device: %s, port: %d" %
|
||||
(str(path), self.num_ports ) )
|
||||
|
||||
backend =self.getBackendInterface(self.backendDomain)
|
||||
|
||||
def cb(blah):
|
||||
log.debug(">UsbifController> Backend created")
|
||||
pass
|
||||
d = backend.connect()
|
||||
d.addCallback(cb) # Chaining the claim port operation
|
||||
return d
|
||||
|
||||
|
||||
def removeDevice(self, path):
|
||||
self.delDevice(path)
|
||||
backend = self.getBackendInterface(self.backendDomain)
|
||||
return backend.send_be_release_port(path)
|
||||
|
||||
def delDevice(self, path):
|
||||
if path in self.devices:
|
||||
del self.devices[path]
|
||||
|
||||
def attachPort(self, path, recreate=0):
|
||||
"""Attach a device to the specified interface.
|
||||
On success the returned deferred will be called with the device.
|
||||
|
||||
@return: deferred
|
||||
@rtype: Deferred
|
||||
"""
|
||||
return self.attachDevice(path)
|
||||
|
||||
def destroy(self):
|
||||
"""Destroy the controller and all devices.
|
||||
"""
|
||||
log.debug("Destroying usbif domain=%d", self.dom)
|
||||
self.destroyBackends()
|
||||
|
||||
def destroyDevices(self):
|
||||
"""Destroy all devices.
|
||||
"""
|
||||
for path in self.getDevices():
|
||||
self.removeDevice(path)
|
||||
|
||||
def destroyBackends(self):
|
||||
for backend in self.getBackendInterfaces():
|
||||
backend.destroy()
|
||||
|
||||
def recv_fe_driver_status_changed(self, msg, req):
|
||||
val = unpackMsg('usbif_fe_driver_status_changed_t', msg)
|
||||
log.debug('>UsbifController>recv_fe_driver_status_changed> %s', str(val))
|
||||
# For each backend?
|
||||
msg = packMsg('usbif_fe_interface_status_changed_t',
|
||||
{ 'status' : USBIF_INTERFACE_STATUS_DISCONNECTED,
|
||||
'domid' : 0, ## FIXME: should be domid of backend
|
||||
'evtchn' : 0 })
|
||||
d = defer.Deferred()
|
||||
d.addCallback(self.disconnected_resp)
|
||||
self.writeRequest(msg)
|
||||
|
||||
def disconnected_resp(self, msg):
|
||||
val = unpackMsg('usbif_fe_interface_status_changed_t', msg)
|
||||
if val['status'] != USBIF_INTERFACE_STATUS_DISCONNECTED:
|
||||
log.error(">UsbifController>disconnected_resp> unexpected status change")
|
||||
else:
|
||||
log.debug(">UsbifController>disconnected_resp> interface disconnected OK")
|
||||
|
||||
def recv_fe_interface_connect(self, msg, req):
|
||||
val = unpackMsg('usbif_fe_interface_status_changed_t', msg)
|
||||
log.debug(">UsbifController>recv_fe_interface_connect> notifying backend")
|
||||
backend = self.getBackendInterfaceByHandle(0)
|
||||
if backend:
|
||||
d = backend.connectInterface(val)
|
||||
else:
|
||||
log.error('>UsbifController>recv_fe_interface_connect> unknown interface')
|
||||
|
||||
def claim_ports(self):
|
||||
backend = self.getBackendInterfaceByHandle(0)
|
||||
for path in self.devices.keys():
|
||||
log.debug(">UsbifController>claim_ports> claiming port... %s" % path)
|
||||
backend.send_be_claim_port(path)
|
||||
|
||||
@@ -151,6 +151,11 @@ gopts.var('pci', val='BUS,DEV,FUNC',
|
||||
For example '-pci c0,02,1a'.
|
||||
The option may be repeated to add more than one pci device.""")
|
||||
|
||||
gopts.var('usb', val='PATH',
|
||||
fn=append_value, default=[],
|
||||
use="""Add a physical USB port to a domain, as specified by the path
|
||||
to that port. This option may be repeated to add more than one port.""")
|
||||
|
||||
gopts.var('ipaddr', val="IPADDR",
|
||||
fn=append_value, default=[],
|
||||
use="Add an IP address to the domain.")
|
||||
@@ -273,6 +278,11 @@ def configure_pci(config_devs, vals):
|
||||
config_pci = ['pci', ['bus', bus], ['dev', dev], ['func', func]]
|
||||
config_devs.append(['device', config_pci])
|
||||
|
||||
def configure_usb(config_devs, vals):
|
||||
for path in vals.usb:
|
||||
config_usb = ['usb', ['path', path]]
|
||||
config_devs.append(['device', config_usb])
|
||||
|
||||
def randomMAC():
|
||||
"""Generate a random MAC address.
|
||||
|
||||
@@ -371,6 +381,7 @@ def make_config(vals):
|
||||
configure_disks(config_devs, vals)
|
||||
configure_pci(config_devs, vals)
|
||||
configure_vifs(config_devs, vals)
|
||||
configure_usb(config_devs, vals)
|
||||
configure_vmx(config_devs, vals)
|
||||
config += config_devs
|
||||
return config
|
||||
|
||||