aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rwxr-xr-xtools/examples/xc_dom_create.py121
-rw-r--r--tools/xend/lib/blkif.py143
-rw-r--r--tools/xend/lib/console.py83
-rw-r--r--tools/xend/lib/domain_controller.h124
-rwxr-xr-xtools/xend/lib/main.py179
-rw-r--r--tools/xend/lib/manager.py89
6 files changed, 560 insertions, 179 deletions
diff --git a/tools/examples/xc_dom_create.py b/tools/examples/xc_dom_create.py
index 799319c6a6..bb9a0576d9 100755
--- a/tools/examples/xc_dom_create.py
+++ b/tools/examples/xc_dom_create.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python
import string, sys, os, time, socket, getopt, signal, syslog
-import Xc, xenctl.utils, xenctl.console_client
+import Xc, xenctl.utils, xenctl.console_client, re
config_dir = '/etc/xc/'
config_file = xc_config_file = config_dir + 'defaults'
@@ -195,6 +195,15 @@ output('VM cmdline : "%s"' % cmdline)
if dryrun:
sys.exit(1)
+##### HACK HACK HACK
+##### Until everyone moves to the new I/O world, and a more robust domain
+##### controller (xend), we use this little trick to discover whether we
+##### are in a testing environment for new I/O stuff.
+new_io_world = True
+for line in os.popen('cat /proc/interrupts').readlines():
+ if re.search('blkdev', line):
+ new_io_world = False
+
##### Code beyond this point is actually used to manage the mechanics of
##### starting (and watching if necessary) guest virtual machines.
@@ -228,14 +237,14 @@ def make_domain():
cmsg = 'new_control_interface(dom='+str(id)+', console_port='+str(console_port)+')'
- xend_response = xenctl.utils.xend_control_message(cmsg)
+ cons_response = xenctl.utils.xend_control_message(cmsg)
- if not xend_response['success']:
+ if not cons_response['success']:
print "Error creating initial event channel"
- print "Error type: " + xend_response['error_type']
- if xend_response['error_type'] == 'exception':
- print "Exception type: " + xend_response['exception_type']
- print "Exception value: " + xend_response['exception_value']
+ print "Error type: " + cons_response['error_type']
+ if cons_response['error_type'] == 'exception':
+ print "Exception type: " + cons_response['exception_type']
+ print "Exception value: " + cons_response['exception_value']
xc.domain_destroy ( dom=id )
sys.exit()
@@ -248,7 +257,7 @@ def make_domain():
sys.exit()
else:
- ret = eval('xc.%s_build ( dom=id, image=image, ramdisk=ramdisk, cmdline=cmdline, control_evtchn=xend_response["remote_port"] )' % builder_fn)
+ ret = eval('xc.%s_build ( dom=id, image=image, ramdisk=ramdisk, cmdline=cmdline, control_evtchn=cons_response["remote_port"] )' % builder_fn)
if ret < 0:
print "Error building Linux guest OS: "
print "Return code = " + str(ret)
@@ -259,6 +268,18 @@ def make_domain():
# set the expertise level appropriately
xenctl.utils.VBD_EXPERT_MODE = vbd_expert
+
+ if new_io_world:
+ cmsg = 'new_block_interface(dom='+str(id)+')'
+ xend_response = xenctl.utils.xend_control_message(cmsg)
+ if not xend_response['success']:
+ print "Error creating block interface"
+ print "Error type: " + xend_response['error_type']
+ if xend_response['error_type'] == 'exception':
+ print "Exception type: " + xend_response['exception_type']
+ print "Exception val: " + xend_response['exception_value']
+ xc.domain_destroy ( dom=id )
+ sys.exit()
for ( uname, virt_name, rw ) in vbd_list:
virt_dev = xenctl.utils.blkdev_name_to_number( virt_name )
@@ -269,42 +290,70 @@ def make_domain():
xc.domain_destroy ( dom=id )
sys.exit()
- # check that setting up this VBD won't violate the sharing
- # allowed by the current VBD expertise level
- if xenctl.utils.vd_extents_validate(segments, rw=='w' or rw=='rw') < 0:
- xc.domain_destroy( dom = id )
- sys.exit()
+ if new_io_world:
+ if len(segments) > 1:
+ print "New I/O world cannot deal with multi-extent vdisks"
+ xc.domain_destroy ( dom=id )
+ sys.exit()
+ seg = segments[0]
+ cmsg = 'new_block_device(dom=' + str(id) + \
+ ',handle=0,vdev=' + str(virt_dev) + \
+ ',pdev=' + str(seg['device']) + \
+ ',start_sect=' + str(seg['start_sector']) + \
+ ',nr_sect=' + str(seg['nr_sectors']) + \
+ ',readonly=' + str(not re.match('w',rw)) + ')'
+ xend_response = xenctl.utils.xend_control_message(cmsg)
+ if not xend_response['success']:
+ print "Error creating virtual block device"
+ print "Error type: " + xend_response['error_type']
+ if xend_response['error_type'] == 'exception':
+ print "Exception type: " + xend_response['exception_type']
+ print "Exception val: " + xend_response['exception_value']
+ xc.domain_destroy ( dom=id )
+ sys.exit()
+ else:
+ # check that setting up this VBD won't violate the sharing
+ # allowed by the current VBD expertise level
+ if xenctl.utils.vd_extents_validate(segments,
+ rw=='w' or rw=='rw') < 0:
+ xc.domain_destroy( dom = id )
+ sys.exit()
- if xc.vbd_create( dom=id, vbd=virt_dev, writeable= rw=='w' or rw=='rw' ):
- print "Error creating VBD vbd=%d writeable=%d\n" % (virt_dev,rw)
- xc.domain_destroy ( dom=id )
- sys.exit()
+ if xc.vbd_create( dom=id, vbd=virt_dev,
+ writeable= rw=='w' or rw=='rw' ):
+ print "Error creating VBD %d (writeable=%d)\n" % (virt_dev,rw)
+ xc.domain_destroy ( dom=id )
+ sys.exit()
- if xc.vbd_setextents( dom=id,
- vbd=virt_dev,
- extents=segments):
- print "Error populating VBD vbd=%d\n" % virt_dev
- xc.domain_destroy ( dom=id )
- sys.exit()
-
- # setup virtual firewall rules for all aliases
- for ip in vfr_ipaddr:
- xenctl.utils.setup_vfr_rules_for_vif( id, 0, ip )
-
- # check for physical device access
- for (pci_bus, pci_dev, pci_func) in pci_device_list:
- if xc.physdev_pci_access_modify(
- dom=id, bus=pci_bus, dev=pci_dev, func=pci_func, enable=1 ) < 0:
- print "Non-fatal error enabling PCI device access."
- else:
- print "Enabled PCI access (%d:%d:%d)." % (pci_bus,pci_dev,pci_func)
+ if xc.vbd_setextents( dom=id,
+ vbd=virt_dev,
+ extents=segments):
+ print "Error populating VBD vbd=%d\n" % virt_dev
+ xc.domain_destroy ( dom=id )
+ sys.exit()
+
+ if not new_io_world:
+ # setup virtual firewall rules for all aliases
+ for ip in vfr_ipaddr:
+ xenctl.utils.setup_vfr_rules_for_vif( id, 0, ip )
+
+ if new_io_world:
+ # check for physical device access
+ for (pci_bus, pci_dev, pci_func) in pci_device_list:
+ if xc.physdev_pci_access_modify(
+ dom=id, bus=pci_bus, dev=pci_dev,
+ func=pci_func, enable=1 ) < 0:
+ print "Non-fatal error enabling PCI device access."
+ else:
+ print "Enabled PCI access (%d:%d:%d)." % \
+ (pci_bus,pci_dev,pci_func)
if xc.domain_start( dom=id ) < 0:
print "Error starting domain"
xc.domain_destroy ( dom=id )
sys.exit()
- return (id, xend_response['console_port'])
+ return (id, cons_response['console_port'])
# end of make_domain()
def mkpidfile():
diff --git a/tools/xend/lib/blkif.py b/tools/xend/lib/blkif.py
new file mode 100644
index 0000000000..94e058f7ce
--- /dev/null
+++ b/tools/xend/lib/blkif.py
@@ -0,0 +1,143 @@
+
+#################################################################
+## xend/blkif.py -- Block-interface management functions for Xend
+## Copyright (c) 2004, K A Fraser (University of Cambridge)
+#################################################################
+
+import errno, re, os, select, signal, socket, struct, sys
+import xend.main, xend.console, xend.manager, xend.utils, Xc
+
+CMSG_BLKIF_BE = 1
+CMSG_BLKIF_FE = 2
+CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED = 0
+CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED = 32
+CMSG_BLKIF_FE_INTERFACE_CONNECT = 33
+CMSG_BLKIF_FE_INTERFACE_DISCONNECT = 34
+CMSG_BLKIF_BE_CREATE = 0
+CMSG_BLKIF_BE_DESTROY = 1
+CMSG_BLKIF_BE_CONNECT = 2
+CMSG_BLKIF_BE_DISCONNECT = 3
+CMSG_BLKIF_BE_VBD_CREATE = 4
+CMSG_BLKIF_BE_VBD_DESTROY = 5
+CMSG_BLKIF_BE_VBD_GROW = 6
+CMSG_BLKIF_BE_VBD_SHRINK = 7
+
+pendmsg = None
+pendaddr = None
+
+def backend_tx_req(msg):
+ port = xend.main.dom0_port
+ if port.space_to_write_request():
+ port.write_request(msg)
+ port.notify()
+ else:
+ xend.blkif.pendmsg = msg
+
+def backend_rx_req(port, msg):
+ port.write_response(msg)
+
+def backend_rx_rsp(port, msg):
+ subtype = (msg.get_header())['subtype']
+ print "Received blkif-be response, subtype %d" % subtype
+ if subtype == CMSG_BLKIF_BE_CREATE:
+ rsp = { 'success': True }
+ xend.main.send_management_response(rsp, xend.blkif.pendaddr)
+ elif subtype == CMSG_BLKIF_BE_CONNECT:
+ (dom,hnd,evtchn,frame,st) = struct.unpack("QIILI", msg.get_payload())
+ blkif = interface.list[xend.main.port_from_dom(dom).local_port]
+ msg = xend.utils.message(CMSG_BLKIF_FE, \
+ CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED, 0)
+ msg.append_payload(struct.pack("III",0,2,blkif.evtchn['port2']))
+ blkif.ctrlif_tx_req(xend.main.port_list[blkif.key], msg)
+ elif subtype == CMSG_BLKIF_BE_VBD_CREATE:
+ (dom,hnd,vdev,ro,st) = struct.unpack("QIHII", msg.get_payload())
+ blkif = interface.list[xend.main.port_from_dom(dom).local_port]
+ (pdev, start_sect, nr_sect, readonly) = blkif.devices[vdev]
+ msg = xend.utils.message(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_GROW, 0)
+ msg.append_payload(struct.pack("QIHHHQQI",dom,0,vdev,0, \
+ pdev,start_sect,nr_sect,0))
+ backend_tx_req(msg)
+ elif subtype == CMSG_BLKIF_BE_VBD_GROW:
+ rsp = { 'success': True }
+ xend.main.send_management_response(rsp, xend.blkif.pendaddr)
+
+def backend_do_work(port):
+ global pendmsg
+ if pendmsg and port.space_to_write_request():
+ port.write_request(pendmsg)
+ pendmsg = None
+ return True
+ return False
+
+
+class interface:
+
+ # Dictionary of all block-device interfaces.
+ list = {}
+
+
+ # NB. 'key' is an opaque value that has no meaning in this class.
+ def __init__(self, dom, key):
+ self.dom = dom
+ self.key = key
+ self.devices = {}
+ self.pendmsg = None
+ interface.list[key] = self
+ msg = xend.utils.message(CMSG_BLKIF_BE, CMSG_BLKIF_BE_CREATE, 0)
+ msg.append_payload(struct.pack("QII",dom,0,0))
+ xend.blkif.pendaddr = xend.main.mgmt_req_addr
+ backend_tx_req(msg)
+
+ # Attach a device to the specified interface
+ def attach_device(self, vdev, pdev, start_sect, nr_sect, readonly):
+ if self.devices.has_key(vdev):
+ return False
+ self.devices[vdev] = (pdev, start_sect, nr_sect, readonly)
+ msg = xend.utils.message(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_CREATE, 0)
+ msg.append_payload(struct.pack("QIHII",self.dom,0,vdev,readonly,0))
+ xend.blkif.pendaddr = xend.main.mgmt_req_addr
+ backend_tx_req(msg)
+ return True
+
+
+ # Completely destroy this interface.
+ def destroy(self):
+ del interface.list[self.key]
+ msg = xend.utils.message(CMSG_BLKIF_BE, CMSG_BLKIF_BE_DESTROY, 0)
+ msg.append_payload(struct.pack("QII",self.dom,0,0))
+ backend_tx_req(msg)
+
+
+ # The parameter @port is the control-interface event channel. This method
+ # returns True if messages were written to the control interface.
+ def ctrlif_transmit_work(self, port):
+ if self.pendmsg and port.space_to_write_request():
+ port.write_request(self.pendmsg)
+ self.pendmsg = None
+ return True
+ return False
+
+ def ctrlif_tx_req(self, port, msg):
+ if port.space_to_write_request():
+ port.write_request(msg)
+ port.notify()
+ else:
+ self.pendmsg = msg
+
+ def ctrlif_rx_req(self, port, msg):
+ port.write_response(msg)
+ subtype = (msg.get_header())['subtype']
+ if subtype == CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED:
+ msg = xend.utils.message(CMSG_BLKIF_FE, \
+ CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED, 0)
+ msg.append_payload(struct.pack("III",0,1,0))
+ self.ctrlif_tx_req(port, msg)
+ elif subtype == CMSG_BLKIF_FE_INTERFACE_CONNECT:
+ (hnd,frame) = struct.unpack("IL", msg.get_payload())
+ xc = Xc.new()
+ self.evtchn = xc.evtchn_bind_interdomain(dom1=0,dom2=self.dom)
+ msg = xend.utils.message(CMSG_BLKIF_BE, \
+ CMSG_BLKIF_BE_CONNECT, 0)
+ msg.append_payload(struct.pack("QIILI",self.dom,0, \
+ self.evtchn['port1'],frame,0))
+ backend_tx_req(msg)
diff --git a/tools/xend/lib/console.py b/tools/xend/lib/console.py
index aad6069979..57898817f5 100644
--- a/tools/xend/lib/console.py
+++ b/tools/xend/lib/console.py
@@ -5,7 +5,7 @@
#############################################################
import errno, re, os, select, signal, socket, struct, sys
-
+import xend.blkif, xend.main, xend.manager, xend.utils, Xc
##
## interface:
@@ -16,7 +16,7 @@ import errno, re, os, select, signal, socket, struct, sys
## CONNECTED: sending/receiving console data on TCP port 'self.port'
##
## A dictionary of all active interfaces, indexed by TCP socket descriptor,
-## is accessible as 'interface.interface_list'.
+## is accessible as 'interface.list_by_fd'.
##
## NB. When a class instance is to be destroyed you *must* call the 'close'
## method. Otherwise a stale reference will eb left in the interface list.
@@ -30,7 +30,11 @@ class interface:
# Dictionary of all active (non-closed) console interfaces.
- interface_list = {}
+ list_by_fd = {}
+
+
+ # Dictionary of all console interfaces, closed and open.
+ list = {}
# NB. 'key' is an opaque value that has no meaning in this class.
@@ -38,6 +42,9 @@ class interface:
self.status = interface.CLOSED
self.port = port
self.key = key
+ self.rbuf = xend.utils.buffer()
+ self.wbuf = xend.utils.buffer()
+ interface.list[key] = self
# Is this interface closed (inactive)?
@@ -58,14 +65,14 @@ class interface:
# Close the interface, if it is not closed already.
def close(self):
if not self.closed():
- del interface.interface_list[self.sock.fileno()]
+ del interface.list_by_fd[self.sock.fileno()]
self.sock.close()
del self.sock
self.status = interface.CLOSED
# Move the interface into the 'listening' state. Opens a new listening
- # socket and updates 'interface_list'.
+ # socket and updates 'list_by_fd'.
def listen(self):
# Close old socket (if any), and create a fresh one.
self.close()
@@ -80,7 +87,7 @@ class interface:
# Announce the new status of thsi interface.
self.status = interface.LISTENING
- interface.interface_list[self.sock.fileno()] = self
+ interface.list_by_fd[self.sock.fileno()] = self
except:
# In case of trouble ensure we get rid of dangling socket reference
@@ -105,7 +112,69 @@ class interface:
# Publish the new socket and the new interface state.
self.sock = sock
self.status = interface.CONNECTED
- interface.interface_list[self.sock.fileno()] = self
+ interface.list_by_fd[self.sock.fileno()] = self
return 1
+ # Completely sestroy a console interface.
+ def destroy(self):
+ self.close()
+ del interface.list[self.key]
+
+
+ # Do work triggered by resource availability on a console-interface socket.
+ def socket_work(self):
+ # If the interface is listening, check for pending connections.
+ if self.listening():
+ self.connect()
+
+ # All done if the interface is not connected.
+ if not self.connected():
+ return
+
+ # Send as much pending data as possible via the socket.
+ while not self.rbuf.empty():
+ try:
+ bytes = self.sock.send(self.rbuf.peek())
+ if bytes > 0:
+ self.rbuf.discard(bytes)
+ except socket.error, error:
+ pass
+
+ # Read as much data as is available. Don't worry about
+ # overflowing our buffer: it's more important to read the
+ # incoming data stream and detect errors or closure of the
+ # remote end in a timely manner.
+ try:
+ while 1:
+ data = self.sock.recv(2048)
+ # Return of zero means the remote end has disconnected.
+ # We therefore return the console interface to listening.
+ if not data:
+ self.listen()
+ break
+ self.wbuf.write(data)
+ except socket.error, error:
+ # Assume that most errors mean that the connection is dead.
+ # In such cases we return the interface to 'listening' state.
+ if error[0] != errno.EAGAIN:
+ print "Better return to listening"
+ self.listen()
+ print "New status: " + str(self.status)
+
+
+ # The parameter @port is the control-interface event channel. This method
+ # returns True if messages were written to the control interface.
+ def ctrlif_transmit_work(self, port):
+ work_done = False
+ while not self.wbuf.empty() and port.space_to_write_request():
+ msg = xend.utils.message(0, 0, 0)
+ msg.append_payload(self.wbuf.read(msg.MAX_PAYLOAD))
+ port.write_request(msg)
+ work_done = True
+ return work_done
+
+
+ def ctrlif_rx_req(self, port, msg):
+ self.rbuf.write(msg.get_payload())
+ port.write_response(msg)
diff --git a/tools/xend/lib/domain_controller.h b/tools/xend/lib/domain_controller.h
index d9ea7d6160..68d4fac1d2 100644
--- a/tools/xend/lib/domain_controller.h
+++ b/tools/xend/lib/domain_controller.h
@@ -76,8 +76,8 @@ typedef struct {
/* Messages from guest to domain controller. */
#define CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED 32
-#define CMSG_BLKIF_FE_INTERFACE_UP 33
-#define CMSG_BLKIF_FE_INTERFACE_DOWN 34
+#define CMSG_BLKIF_FE_INTERFACE_CONNECT 33
+#define CMSG_BLKIF_FE_INTERFACE_DISCONNECT 34
/* These are used by both front-end and back-end drivers. */
#define blkif_vdev_t u16
@@ -91,13 +91,13 @@ typedef struct {
* 1. The shared-memory frame is available for reuse.
* 2. Any unacknowledged messgaes pending on the interface were dropped.
*/
-#define BLKIF_INTERFACE_STATUS_DESTROYED 0 /* Interface doesn't exist. */
-#define BLKIF_INTERFACE_STATUS_DOWN 1 /* Interface exists but is down. */
-#define BLKIF_INTERFACE_STATUS_UP 2 /* Interface exists and is up. */
+#define BLKIF_INTERFACE_STATUS_DESTROYED 0 /* Interface doesn't exist. */
+#define BLKIF_INTERFACE_STATUS_DISCONNECTED 1 /* Exists but is disconnected. */
+#define BLKIF_INTERFACE_STATUS_CONNECTED 2 /* Exists and is connected. */
typedef struct {
unsigned int handle;
unsigned int status;
- unsigned int evtchn; /* status == BLKIF_INTERFACE_STATUS_UP */
+ unsigned int evtchn; /* status == BLKIF_INTERFACE_STATUS_CONNECTED */
} blkif_fe_interface_status_changed_t;
/*
@@ -109,30 +109,37 @@ typedef struct {
* If the driver goes DOWN while interfaces are still UP, the domain
* will automatically take the interfaces DOWN.
*/
-#define BLKIF_DRIVER_STATUS_DOWN 0
-#define BLKIF_DRIVER_STATUS_UP 1
+#define BLKIF_DRIVER_STATUS_DOWN 0
+#define BLKIF_DRIVER_STATUS_UP 1
typedef struct {
unsigned int status; /* BLKIF_DRIVER_STATUS_??? */
} blkif_fe_driver_status_changed_t;
/*
- * CMSG_BLKIF_FE_INTERFACE_UP:
- * If successful, the domain controller will acknowledge with a STATUS_UP
- * message.
+ * CMSG_BLKIF_FE_INTERFACE_CONNECT:
+ * If successful, the domain controller will acknowledge with a
+ * STATUS_CONNECTED message.
*/
typedef struct {
unsigned int handle;
unsigned long shmem_frame;
-} blkif_fe_interface_up_t;
+} blkif_fe_interface_connect_t;
/*
- * CMSG_BLKIF_FE_INTERFACE_DOWN:
- * If successful, the domain controller will acknowledge with a STATUS_DOWN
- * message.
+ * CMSG_BLKIF_FE_INTERFACE_DISCONNECT:
+ * If successful, the domain controller will acknowledge with a
+ * STATUS_DISCONNECTED message.
*/
typedef struct {
+ /* IN */
unsigned int handle;
-} blkif_fe_interface_down_t;
+ /* OUT */
+ /*
+ * Tells driver how many interfaces it should expect to immediately
+ * receive notifications about.
+ */
+ unsigned int nr_interfaces;
+} blkif_fe_interface_disconnect_t;
/******************************************************************************
@@ -142,10 +149,12 @@ typedef struct {
/* Messages from domain controller. */
#define CMSG_BLKIF_BE_CREATE 0 /* Create a new block-device interface. */
#define CMSG_BLKIF_BE_DESTROY 1 /* Destroy a block-device interface. */
-#define CMSG_BLKIF_BE_VBD_CREATE 2 /* Create a new VBD for an interface. */
-#define CMSG_BLKIF_BE_VBD_DESTROY 3 /* Delete a VBD from an interface. */
-#define CMSG_BLKIF_BE_VBD_GROW 4 /* Append an extent to a given VBD. */
-#define CMSG_BLKIF_BE_VBD_SHRINK 5 /* Remove last extent from a given VBD. */
+#define CMSG_BLKIF_BE_CONNECT 2 /* Connect i/f to remote driver. */
+#define CMSG_BLKIF_BE_DISCONNECT 3 /* Disconnect i/f from remote driver. */
+#define CMSG_BLKIF_BE_VBD_CREATE 4 /* Create a new VBD for an interface. */
+#define CMSG_BLKIF_BE_VBD_DESTROY 5 /* Delete a VBD from an interface. */
+#define CMSG_BLKIF_BE_VBD_GROW 6 /* Append an extent to a given VBD. */
+#define CMSG_BLKIF_BE_VBD_SHRINK 7 /* Remove last extent from a given VBD. */
/* Messages to domain controller. */
#define CMSG_BLKIF_BE_DRIVER_STATUS_CHANGED 32
@@ -167,36 +176,36 @@ typedef struct {
/* The following are specific error returns. */
#define BLKIF_BE_STATUS_INTERFACE_EXISTS 2
#define BLKIF_BE_STATUS_INTERFACE_NOT_FOUND 3
-#define BLKIF_BE_STATUS_VBD_EXISTS 4
-#define BLKIF_BE_STATUS_VBD_NOT_FOUND 5
-#define BLKIF_BE_STATUS_OUT_OF_MEMORY 6
-#define BLKIF_BE_STATUS_EXTENT_NOT_FOUND 7
-#define BLKIF_BE_STATUS_MAPPING_ERROR 8
+#define BLKIF_BE_STATUS_INTERFACE_CONNECTED 4
+#define BLKIF_BE_STATUS_VBD_EXISTS 5
+#define BLKIF_BE_STATUS_VBD_NOT_FOUND 6
+#define BLKIF_BE_STATUS_OUT_OF_MEMORY 7
+#define BLKIF_BE_STATUS_EXTENT_NOT_FOUND 8
+#define BLKIF_BE_STATUS_MAPPING_ERROR 9
/* This macro can be used to create an array of descriptive error strings. */
-#define BLKIF_BE_STATUS_ERRORS { \
- "Okay", \
- "Non-specific error", \
- "Interface already exists", \
- "Interface not found", \
- "VBD already exists", \
- "VBD not found", \
- "Out of memory", \
- "Extent not found for VBD", \
+#define BLKIF_BE_STATUS_ERRORS { \
+ "Okay", \
+ "Non-specific error", \
+ "Interface already exists", \
+ "Interface not found", \
+ "Interface is still connected", \
+ "VBD already exists", \
+ "VBD not found", \
+ "Out of memory", \
+ "Extent not found for VBD", \
"Could not map domain memory" }
/*
* CMSG_BLKIF_BE_CREATE:
* When the driver sends a successful response then the interface is fully
- * set up. The controller will send an UP notification to the front-end
+ * created. The controller will send a DOWN notification to the front-end
* driver.
*/
typedef struct {
/* IN */
domid_t domid; /* Domain attached to new interface. */
unsigned int blkif_handle; /* Domain-specific interface handle. */
- unsigned int evtchn; /* Event channel for notifications. */
- unsigned long shmem_frame; /* Page cont. shared comms window. */
/* OUT */
unsigned int status;
} blkif_be_create_t;
@@ -204,8 +213,8 @@ typedef struct {
/*
* CMSG_BLKIF_BE_DESTROY:
* When the driver sends a successful response then the interface is fully
- * torn down. The controller will send a DOWN notification to the front-end
- * driver.
+ * torn down. The controller will send a DESTROYED notification to the
+ * front-end driver.
*/
typedef struct {
/* IN */
@@ -215,6 +224,36 @@ typedef struct {
unsigned int status;
} blkif_be_destroy_t;
+/*
+ * CMSG_BLKIF_BE_CONNECT:
+ * When the driver sends a successful response then the interface is fully
+ * connected. The controller will send a CONNECTED notification to the
+ * front-end driver.
+ */
+typedef struct {
+ /* IN */
+ domid_t domid; /* Domain attached to new interface. */
+ unsigned int blkif_handle; /* Domain-specific interface handle. */
+ unsigned int evtchn; /* Event channel for notifications. */
+ unsigned long shmem_frame; /* Page cont. shared comms window. */
+ /* OUT */
+ unsigned int status;
+} blkif_be_connect_t;
+
+/*
+ * CMSG_BLKIF_BE_DISCONNECT:
+ * When the driver sends a successful response then the interface is fully
+ * disconnected. The controller will send a DOWN notification to the front-end
+ * driver.
+ */
+typedef struct {
+ /* IN */
+ domid_t domid; /* Domain attached to new interface. */
+ unsigned int blkif_handle; /* Domain-specific interface handle. */
+ /* OUT */
+ unsigned int status;
+} blkif_be_disconnect_t;
+
/* CMSG_BLKIF_BE_VBD_CREATE */
typedef struct {
/* IN */
@@ -264,7 +303,14 @@ typedef struct {
* will automatically send DOWN notifications.
*/
typedef struct {
+ /* IN */
unsigned int status; /* BLKIF_DRIVER_STATUS_??? */
+ /* OUT */
+ /*
+ * Tells driver how many interfaces it should expect to immediately
+ * receive notifications about.
+ */
+ unsigned int nr_interfaces;
} blkif_be_driver_status_changed_t;
#endif /* __DOMAIN_CONTROLLER_H__ */
diff --git a/tools/xend/lib/main.py b/tools/xend/lib/main.py
index b870af55d1..7b5adbab83 100755
--- a/tools/xend/lib/main.py
+++ b/tools/xend/lib/main.py
@@ -5,7 +5,7 @@
###########################################################
import errno, re, os, pwd, select, signal, socket, struct, sys, time
-import xend.console, xend.manager, xend.utils, Xc
+import xend.blkif, xend.console, xend.manager, xend.utils, Xc
# The following parameters could be placed in a configuration file.
@@ -16,13 +16,35 @@ CONTROL_DIR = '/var/run/xend'
UNIX_SOCK = 'management_sock' # relative to CONTROL_DIR
+CMSG_CONSOLE = 0
+CMSG_BLKIF_BE = 1
+CMSG_BLKIF_FE = 2
+
+
+def port_from_dom(dom):
+ global port_list
+ for idx, port in port_list.items():
+ if port.remote_dom == dom:
+ return port
+ return None
+
+
+def send_management_response(response, addr):
+ try:
+ response = str(response)
+ print "Mgmt_rsp[%s]: %s" % (addr, response)
+ management_interface.sendto(response, addr)
+ except socket.error, error:
+ pass
+
+
def daemon_loop():
# Could we do this more nicely? The xend.manager functions need access
# to this global state to do their work.
- global control_list, notifier
+ global port_list, notifier, management_interface, mgmt_req_addr, dom0_port
- # List of all control interfaces, indexed by local event-channel port.
- control_list = {}
+ # Lists of all interfaces, indexed by local event-channel port.
+ port_list = {}
xc = Xc.new()
@@ -46,13 +68,10 @@ def daemon_loop():
# The DOM0 control interface is not set up via the management interface.
# Note that console messages don't come our way (actually, only driver
- # back-ends should use the DOM0 control interface) -- the console
- # structures are dummies.
+ # back-ends should use the DOM0 control interface).
dom0_port = xend.utils.port(0)
- xend.main.notifier.bind(dom0_port.local_port)
- xend.main.control_list[dom0_port.local_port] = \
- (dom0_port, xend.utils.buffer(), xend.utils.buffer(), \
- xend.console.interface(0, dom0_port.local_port))
+ notifier.bind(dom0_port.local_port)
+ port_list[dom0_port.local_port] = dom0_port
##
## MAIN LOOP
@@ -68,10 +87,10 @@ def daemon_loop():
waitset = select.poll()
waitset.register(management_interface, select.POLLIN)
waitset.register(notifier, select.POLLIN)
- for idx, (port, rbuf, wbuf, con_if) in control_list.items():
+ for idx, con_if in xend.console.interface.list_by_fd.items():
if not con_if.closed():
pflags = select.POLLIN
- if not rbuf.empty() and con_if.connected():
+ if not con_if.rbuf.empty() and con_if.connected():
pflags = select.POLLIN | select.POLLOUT
waitset.register(con_if.sock.fileno(), pflags)
@@ -82,16 +101,16 @@ def daemon_loop():
# These should consist of executable Python statements that call
# well-known management functions (e.g., new_control_interface(dom=9)).
try:
- data, addr = management_interface.recvfrom(2048)
+ data, mgmt_req_addr = management_interface.recvfrom(2048)
except socket.error, error:
if error[0] != errno.EAGAIN:
raise
else:
- if addr:
+ if mgmt_req_addr:
# Evaluate the request in an exception-trapping sandbox.
try:
- print "Mgmt_req[%s]: %s" % (addr, data)
- response = str(eval('xend.manager.'+data))
+ print "Mgmt_req[%s]: %s" % (mgmt_req_addr, data)
+ response = eval('xend.manager.'+data)
except:
# Catch all exceptions and turn into an error response:
@@ -107,69 +126,20 @@ def daemon_loop():
response = str(response)
# Try to send a response to the requester.
- try:
- print "Mgmt_rsp[%s]: %s" % (addr, response)
- management_interface.sendto(response, addr)
- except socket.error, error:
- pass
+ if response:
+ send_management_response(response, mgmt_req_addr)
# Do work for every console interface that hit in the poll set.
for (fd, events) in fdset:
- if not xend.console.interface.interface_list.has_key(fd):
- continue
- con_if = xend.console.interface.interface_list[fd]
-
- # If the interface is listening, check for pending connections.
- if con_if.listening():
- con_if.connect()
-
- # All done if the interface is not connected.
- if not con_if.connected():
- continue
- (port, rbuf, wbuf, con_if) = control_list[con_if.key]
-
- # Send as much pending data as possible via the socket.
- while not rbuf.empty():
- try:
- bytes = con_if.sock.send(rbuf.peek())
- if bytes > 0:
- rbuf.discard(bytes)
- except socket.error, error:
- pass
-
- # Read as much data as is available. Don't worry about
- # overflowing our buffer: it's more important to read the
- # incoming data stream and detect errors or closure of the
- # remote end in a timely manner.
- try:
- while 1:
- data = con_if.sock.recv(2048)
- # Return of zero means the remote end has disconnected.
- # We therefore return the console interface to listening.
- if not data:
- con_if.listen()
- break
- wbuf.write(data)
- except socket.error, error:
- # Assume that most errors mean that the connection is dead.
- # In such cases we return the interface to 'listening' state.
- if error[0] != errno.EAGAIN:
- print "Better return to listening"
- con_if.listen()
- print "New status: " + str(con_if.status)
-
- # We may now have pending data to send via the relevant
- # inter-domain control interface. If so then we send all we can
- # and notify the remote end.
- work_done = False
- while not wbuf.empty() and port.space_to_write_request():
- msg = xend.utils.message(0, 0, 0)
- msg.append_payload(wbuf.read(msg.MAX_PAYLOAD))
- port.write_request(msg)
- work_done = True
- if work_done:
- port.notify()
-
+ if xend.console.interface.list_by_fd.has_key(fd):
+ con_if = xend.console.interface.list_by_fd[fd]
+ con_if.socket_work()
+ # We may now have pending data to send via the control
+ # interface. If so then send all we can and notify the remote.
+ port = port_list[con_if.key]
+ if con_if.ctrlif_transmit_work(port):
+ port.notify()
+
# Process control-interface notifications from other guest OSes.
while 1:
# Grab a notification, if there is one.
@@ -178,42 +148,69 @@ def daemon_loop():
break
(idx, type) = notification
- if not control_list.has_key(idx):
+ if not port_list.has_key(idx):
continue
- (port, rbuf, wbuf, con_if) = control_list[idx]
+ port = port_list[idx]
work_done = False
+ con_if = False
+ if xend.console.interface.list.has_key(idx):
+ con_if = xend.console.interface.list[idx]
+
+ blk_if = False
+ if xend.blkif.interface.list.has_key(idx):
+ blk_if = xend.blkif.interface.list[idx]
+
# If we pick up a disconnect notification then we do any necessary
# cleanup.
if type == notifier.EXCEPTION:
ret = xc.evtchn_status(idx)
if ret['status'] == 'unbound':
notifier.unbind(idx)
- con_if.close()
- del control_list[idx], port, rbuf, wbuf, con_if
+ del port_list[idx], port
+ if con_if:
+ con_if.destroy()
+ del con_if
+ if blk_if:
+ blk_if.destroy()
+ del blk_if
continue
- # Read incoming requests. Currently assume that request
- # message always containb console data.
+ # Process incoming requests.
while port.request_to_read():
msg = port.read_request()
- rbuf.write(msg.get_payload())
- port.write_response(msg)
work_done = True
-
- # Incoming responses are currently thrown on the floor.
+ type = (msg.get_header())['type']
+ if type == CMSG_CONSOLE and con_if:
+ con_if.ctrlif_rx_req(port, msg)
+ elif type == CMSG_BLKIF_FE and blk_if:
+ blk_if.ctrlif_rx_req(port, msg)
+ elif type == CMSG_BLKIF_BE and port == dom0_port:
+ xend.blkif.backend_rx_req(port, msg)
+ else:
+ port.write_response(msg)
+
+ # Process incoming responses.
while port.response_to_read():
msg = port.read_response()
work_done = True
+ type = (msg.get_header())['type']
+ if type == CMSG_BLKIF_BE and port == dom0_port:
+ xend.blkif.backend_rx_rsp(port, msg)
+
+ # Send console data.
+ if con_if and con_if.ctrlif_transmit_work(port):
+ work_done = True
- # Send as much pending console data as there is room for.
- while not wbuf.empty() and port.space_to_write_request():
- msg = xend.utils.message(0, 0, 0)
- msg.append_payload(wbuf.read(msg.MAX_PAYLOAD))
- port.write_request(msg)
+ # Send blkif messages.
+ if blk_if and blk_if.ctrlif_transmit_work(port):
work_done = True
+ # Back-end block-device work.
+ if port == dom0_port and xend.blkif.backend_do_work(port):
+ work_done = True
+
# Finally, notify the remote end of any work that we did.
if work_done:
port.notify()
diff --git a/tools/xend/lib/manager.py b/tools/xend/lib/manager.py
index 42d66d3a95..ea7398cd4c 100644
--- a/tools/xend/lib/manager.py
+++ b/tools/xend/lib/manager.py
@@ -4,13 +4,13 @@
## Copyright (c) 2004, K A Fraser (University of Cambridge)
#############################################################
-import xend.console, xend.main, xend.utils
+import xend.blkif, xend.console, xend.main, xend.utils
##
## new_control_interface:
-## Create a new control interface with the specified domain 'dom'.
-## The console port may also be specified; otehrwise a suitable port is
+## Create a new control interface with the specified domain @dom.
+## The console port may also be specified; otherwise a suitable port is
## automatically allocated.
##
def new_control_interface(dom, console_port=-1):
@@ -26,9 +26,8 @@ def new_control_interface(dom, console_port=-1):
con_if = xend.console.interface(console_port, port.local_port)
con_if.listen()
- # Add control state to the master list.
- xend.main.control_list[port.local_port] = \
- (port, xend.utils.buffer(), xend.utils.buffer(), con_if)
+ # Update the master port list.
+ xend.main.port_list[port.local_port] = port
# Construct the successful response to be returned to the requester.
response = { 'success': True }
@@ -36,3 +35,81 @@ def new_control_interface(dom, console_port=-1):
response['remote_port'] = port.remote_port
response['console_port'] = console_port
return response
+
+
+##
+## new_block_interface:
+## Create a new block interface for the specified domain @dom.
+##
+def new_block_interface(dom, handle=-1):
+ # By default we create an interface with handle zero.
+ if handle < 0:
+ handle = 0
+
+ # We only support one interface per domain, which must have handle zero.
+ if handle != 0:
+ response = { 'success': False }
+ response['error_type'] = 'Bad handle %d (only handle 0 ' + \
+ 'is supported)' % handle
+ return response
+
+ # Find local event-channel port associated with the specified domain.
+ port = xend.main.port_from_dom(dom)
+ if not port:
+ response = { 'success': False }
+ response['error_type'] = 'Unknown domain %d' % dom
+ return response
+
+ # The interface must not already exist.
+ if xend.blkif.interface.list.has_key(port.local_port):
+ response = { 'success': False }
+ response['error_type'] = 'Interface (dom=%d,handle=%d) already ' + \
+ 'exists' % (dom, handle)
+ return response
+
+ # Create the new interface. Initially no virtual devices are attached.
+ xend.blkif.interface(dom, port.local_port)
+
+ # Response is deferred until back-end driver sends acknowledgement.
+ return None
+
+
+##
+## new_block_device:
+## Attach a new virtual block device to the specified block interface
+## (@dom, @handle). The new device is identified by @vdev, and maps to
+## the real block extent (@pdev, @start_sect, @nr_sect). If @readonly then
+## write requests to @vdev will be rejected.
+##
+def new_block_device(dom, handle, vdev, pdev, start_sect, nr_sect, readonly):
+ # We only support one interface per domain, which must have handle zero.
+ if handle != 0:
+ response = { 'success': False }
+ response['error_type'] = 'Bad handle %d (only handle 0 ' + \
+ 'is supported)' % handle
+ return response
+
+ # Find local event-channel port associated with the specified domain.
+ port = xend.main.port_from_dom(dom)
+ if not port:
+ response = { 'success': False }
+ response['error_type'] = 'Unknown domain %d' % dom
+ return response
+
+ # The interface must exist.
+ if not xend.blkif.interface.list.has_key(port.local_port):
+ response = { 'success': False }
+ response['error_type'] = 'Interface (dom=%d,handle=%d) does not ' + \
+ 'exists' % (dom, handle)
+ return response
+
+ # The virtual device must not yet exist.
+ blkif = xend.blkif.interface.list[port.local_port]
+ if not blkif.attach_device(vdev, pdev, start_sect, nr_sect, readonly):
+ response = { 'success': False }
+ response['error_type'] = 'Vdevice (dom=%d,handle=%d,vdevice=%d) ' + \
+ 'already exists' % (dom, handle, vdev)
+ return response
+
+ # Response is deferred until back-end driver sends acknowledgement.
+ return None