debuggers.hg

view tools/python/xen/xend/server/blkif.py @ 3637:0ebbb4b2bbdc

bitkeeper revision 1.1159.223.56 (41fffbb0baSUSOWVCJ_0NFmEtCmUkw)

Fix the device number calculation for /dev/hd* device names;
ide device numbering works differently to scsi

Signed-off-by: Jody Belka <knew-xen@pimb.org>
Signed-off-by: ian.pratt@cl.cam.ac.uk
author iap10@labyrinth.cl.cam.ac.uk
date Tue Feb 01 21:59:12 2005 +0000 (2005-02-01)
parents 565871eff231
children 11b95fad5f5d c07e188e4cea e194000fb445
line source
1 # Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
2 """Support for virtual block devices.
3 """
5 from twisted.internet import defer
7 from xen.xend import sxp
8 from xen.xend import Blkctl
9 from xen.xend.XendLogging import log
10 from xen.xend.XendError import XendError, VmError
12 import os
13 import re
14 import string
15 import channel
16 import controller
17 from messages import *
19 from xen.util.ip import _readline, _readlines
21 def expand_dev_name(name):
22 if re.match( '^/dev/', name ):
23 return name
24 else:
25 return '/dev/' + name
27 def check_mounted(self, name):
28 mode = None
29 name = expand_dev_name(name)
30 lines = _readlines(os.popen('mount 2>/dev/null'))
31 exp = re.compile('^' + name + ' .*[\(,]r(?P<mode>[ow])[,\)]')
32 for line in lines:
33 pm = exp.match(line)
34 if not pm: continue
35 mode = pm.group('mode')
36 break
37 if mode is 'w':
38 return mode
39 if mode is 'o':
40 mode = 'r'
41 blkifs = self.ctrl.daemon.blkifs()
42 for blkif in blkifs:
43 if blkif[1][1] is self.ctrl.dom:
44 continue
45 for dev in self.ctrl.daemon.blkif_get(blkif[1][1]).getDevices():
46 if dev.type == 'phy' and name == expand_dev_name(dev.params):
47 mode = dev.mode
48 if 'w' in mode:
49 return 'w'
50 if mode and 'r' in mode:
51 return 'r'
52 return None
54 def blkdev_name_to_number(name):
55 """Take the given textual block-device name (e.g., '/dev/sda1',
56 'hda') and return the device number used by the OS. """
58 n = expand_dev_name(name)
60 try:
61 return os.stat(n).st_rdev
62 except Exception, ex:
63 log.debug("exception looking up device number for %s: %s", name, ex)
64 pass
66 if re.match( '/dev/sd[a-p]([0-9]|1[0-5])', n):
67 return 8 * 256 + 16 * (ord(n[7:8]) - ord('a')) + int(n[8:])
69 if re.match( '/dev/hd[a-t]([1-9]|[1-5][0-9]|6[0-3])?', n):
70 ide_majors = [ 3, 22, 33, 34, 56, 57, 88, 89, 90, 91 ]
71 major = ide_majors[(ord(n[7:8]) - ord('a')) / 2]
72 minor = ((ord(n[7:8]) - ord('a')) % 2) * 64 + int(n[8:] or 0)
73 return major * 256 + minor
75 # see if this is a hex device number
76 if re.match( '^(0x)?[0-9a-fA-F]+$', name ):
77 return string.atoi(name,16)
79 return None
81 def blkdev_segment(name):
82 """Take the given block-device name (e.g. '/dev/sda1', 'hda')
83 and return a dictionary { device, start_sector,
84 nr_sectors, type }
85 device: Device number of the given partition
86 start_sector: Index of first sector of the partition
87 nr_sectors: Number of sectors comprising this partition
88 type: 'Disk' or identifying name for partition type
89 """
90 val = None
91 n = blkdev_name_to_number(name)
92 if n:
93 val = { 'device' : n,
94 'start_sector' : long(0),
95 'nr_sectors' : long(1L<<63),
96 'type' : 'Disk' }
97 return val
99 class BlkifBackendController(controller.BackendController):
100 """ Handler for the 'back-end' channel to a block device driver domain.
101 """
103 def __init__(self, factory, dom):
104 controller.BackendController.__init__(self, factory, dom)
105 self.addMethod(CMSG_BLKIF_BE,
106 CMSG_BLKIF_BE_DRIVER_STATUS,
107 self.recv_be_driver_status)
108 self.registerChannel()
110 def recv_be_driver_status(self, msg, req):
111 """Request handler for be_driver_status messages.
113 @param msg: message
114 @type msg: xu message
115 @param req: request flag (true if the msg is a request)
116 @type req: bool
117 """
118 val = unpackMsg('blkif_be_driver_status_t', msg)
119 status = val['status']
121 class BlkifBackendInterface(controller.BackendInterface):
122 """ Handler for the 'back-end' channel to a block device driver domain
123 on behalf of a front-end domain.
124 Must be connected using connect() before it can be used.
125 Do not create directly - use getBackendInterface() on the BlkifController.
126 """
128 def __init__(self, ctrl, dom, handle):
129 controller.BackendInterface.__init__(self, ctrl, dom, handle)
130 self.connected = 0
131 self.evtchn = None
132 self.status = BLKIF_INTERFACE_STATUS_DISCONNECTED
134 def __str__(self):
135 return '<BlkifBackendInterface %d %d>' % (self.controller.dom, self.dom)
137 def getEventChannelBackend(self):
138 val = 0
139 if self.evtchn:
140 val = self.evtchn['port1']
141 return val
143 def getEventChannelFrontend(self):
144 val = 0
145 if self.evtchn:
146 val = self.evtchn['port2']
147 return val
149 def connect(self, recreate=0):
150 """Connect to the blkif control interface.
152 @param recreate: true if after xend restart
153 @return: deferred
154 """
155 log.debug("Connecting blkif %s", str(self))
156 if recreate or self.connected:
157 d = defer.succeed(self)
158 else:
159 d = self.send_be_create()
160 d.addCallback(self.respond_be_create)
161 return d
163 def send_be_create(self):
164 d = defer.Deferred()
165 msg = packMsg('blkif_be_create_t',
166 { 'domid' : self.controller.dom,
167 'blkif_handle' : self.handle })
168 self.writeRequest(msg, response=d)
169 return d
171 def respond_be_create(self, msg):
172 val = unpackMsg('blkif_be_create_t', msg)
173 self.connected = 1
174 return self
176 def destroy(self):
177 """Disconnect from the blkif control interface and destroy it.
178 """
179 def cb_destroy(val):
180 self.send_be_destroy()
181 self.close()
182 d = defer.Deferred()
183 d.addCallback(cb_destroy)
184 self.send_be_disconnect(response=d)
186 def send_be_disconnect(self, response=None):
187 msg = packMsg('blkif_be_disconnect_t',
188 { 'domid' : self.controller.dom,
189 'blkif_handle' : self.handle })
190 self.writeRequest(msg, response=response)
192 def send_be_destroy(self, response=None):
193 msg = packMsg('blkif_be_destroy_t',
194 { 'domid' : self.controller.dom,
195 'blkif_handle' : self.handle })
196 self.writeRequest(msg, response=response)
198 def connectInterface(self, val):
199 self.evtchn = channel.eventChannel(self.dom, self.controller.dom)
200 log.debug("Connecting blkif to event channel %s ports=%d:%d",
201 str(self), self.evtchn['port1'], self.evtchn['port2'])
202 msg = packMsg('blkif_be_connect_t',
203 { 'domid' : self.controller.dom,
204 'blkif_handle' : self.handle,
205 'evtchn' : self.getEventChannelBackend(),
206 'shmem_frame' : val['shmem_frame'] })
207 d = defer.Deferred()
208 d.addCallback(self.respond_be_connect)
209 self.writeRequest(msg, response=d)
211 def respond_be_connect(self, msg):
212 """Response handler for a be_connect message.
214 @param msg: message
215 @type msg: xu message
216 """
217 val = unpackMsg('blkif_be_connect_t', msg)
218 self.status = BLKIF_INTERFACE_STATUS_CONNECTED
219 self.send_fe_interface_status()
221 def send_fe_interface_status(self, response=None):
222 msg = packMsg('blkif_fe_interface_status_t',
223 { 'handle' : self.handle,
224 'status' : self.status,
225 'domid' : self.dom,
226 'evtchn' : self.getEventChannelFrontend() })
227 self.controller.writeRequest(msg, response=response)
229 def interfaceDisconnected(self):
230 self.status = BLKIF_INTERFACE_STATUS_DISCONNECTED
231 #todo?: Do this: self.evtchn = None
232 self.send_fe_interface_status()
234 def interfaceChanged(self):
235 """Notify the front-end that devices have been added or removed.
236 The front-end should then probe for devices.
237 """
238 msg = packMsg('blkif_fe_interface_status_t',
239 { 'handle' : self.handle,
240 'status' : BLKIF_INTERFACE_STATUS_CHANGED,
241 'domid' : self.dom,
242 'evtchn' : 0 })
243 self.controller.writeRequest(msg)
245 class BlkifControllerFactory(controller.SplitControllerFactory):
246 """Factory for creating block device interface controllers.
247 """
249 def __init__(self):
250 controller.SplitControllerFactory.__init__(self)
252 def createController(self, dom, recreate=0):
253 """Create a block device controller for a domain.
255 @param dom: domain
256 @type dom: int
257 @param recreate: if true it's a recreate (after xend restart)
258 @type recreate: bool
259 @return: block device controller
260 @rtype: BlkifController
261 """
262 blkif = self.getControllerByDom(dom)
263 if blkif is None:
264 blkif = BlkifController(self, dom)
265 self.addController(blkif)
266 return blkif
268 def createBackendController(self, dom):
269 """Create a block device backend controller.
271 @param dom: backend domain
272 @return: backend controller
273 """
274 return BlkifBackendController(self, dom)
276 def createBackendInterface(self, ctrl, dom, handle):
277 """Create a block device backend interface.
279 @param ctrl: controller
280 @param dom: backend domain
281 @param handle: interface handle
282 @return: backend interface
283 """
284 return BlkifBackendInterface(ctrl, dom, handle)
286 def getDomainDevices(self, dom):
287 """Get the block devices for a domain.
289 @param dom: domain
290 @type dom: int
291 @return: devices
292 @rtype: [device]
293 """
294 blkif = self.getControllerByDom(dom)
295 return (blkif and blkif.getDevices()) or []
297 def getDomainDevice(self, dom, idx):
298 """Get a block device from a domain.
300 @param dom: domain
301 @type dom: int
302 @param idx: device index
303 @type idx: int
304 @return: device
305 @rtype: device
306 """
307 blkif = self.getControllerByDom(dom)
308 return (blkif and blkif.getDevice(idx)) or None
310 class BlkDev(controller.SplitDev):
311 """Info record for a block device.
312 """
314 def __init__(self, idx, ctrl, config):
315 controller.SplitDev.__init__(self, idx, ctrl)
316 self.dev = None
317 self.uname = None
318 self.vdev = None
319 self.mode = None
320 self.type = None
321 self.params = None
322 self.node = None
323 self.device = None
324 self.start_sector = None
325 self.nr_sectors = None
326 self.ctrl = ctrl
327 self.configure(config)
329 def configure(self, config):
330 self.config = config
331 self.uname = sxp.child_value(config, 'uname')
332 if not self.uname:
333 raise VmError('vbd: Missing uname')
334 # Split into type and type-specific params (which are passed to the
335 # type-specific control script).
336 (self.type, self.params) = string.split(self.uname, ':', 1)
337 self.dev = sxp.child_value(config, 'dev')
338 if not self.dev:
339 raise VmError('vbd: Missing dev')
340 self.mode = sxp.child_value(config, 'mode', 'r')
341 # todo: The 'dev' should be looked up in the context of the domain.
342 self.vdev = blkdev_name_to_number(self.dev)
343 if not self.vdev:
344 raise VmError('vbd: Device not found: %s' % self.dev)
345 try:
346 self.backendDomain = int(sxp.child_value(config, 'backend', '0'))
347 except:
348 raise XendError('invalid backend domain')
350 def recreate(self, savedinfo):
351 node = sxp.child_value(savedinfo, 'node')
352 self.setNode(node)
354 def attach(self):
355 node = Blkctl.block('bind', self.type, self.params)
356 self.setNode(node)
357 return self.attachBackend()
359 def unbind(self):
360 if self.node is None: return
361 log.debug("Unbinding vbd (type %s) from %s"
362 % (self.type, self.node))
363 Blkctl.block('unbind', self.type, self.node)
365 def setNode(self, node):
366 mounted_mode = check_mounted(self, node)
367 if not '!' in self.mode and mounted_mode:
368 if mounted_mode is "w":
369 raise VmError("vbd: Segment %s is in writable use" %
370 self.uname)
371 elif 'w' in self.mode:
372 raise VmError("vbd: Segment %s is in read-only use" %
373 self.uname)
374 segment = blkdev_segment(node)
375 if not segment:
376 raise VmError("vbd: Segment not found: uname=%s" % self.uname)
377 self.node = node
378 self.device = segment['device']
379 self.start_sector = segment['start_sector']
380 self.nr_sectors = segment['nr_sectors']
382 def readonly(self):
383 return 'w' not in self.mode
385 def sxpr(self):
386 val = ['vbd',
387 ['idx', self.idx],
388 ['vdev', self.vdev],
389 ['device', self.device],
390 ['mode', self.mode]]
391 if self.dev:
392 val.append(['dev', self.dev])
393 if self.uname:
394 val.append(['uname', self.uname])
395 if self.node:
396 val.append(['node', self.node])
397 if self.index is not None:
398 val.append(['index', self.index])
399 return val
401 def destroy(self, change=0):
402 """Destroy the device. If 'change' is true notify the front-end interface.
404 @param change: change flag
405 """
406 log.debug("Destroying vbd domain=%d idx=%s", self.controller.dom, self.idx)
407 d = self.send_be_vbd_destroy()
408 if change:
409 d.addCallback(lambda val: self.interfaceChanged())
410 d.addCallback(lambda val: self.unbind())
412 def interfaceChanged(self):
413 """Tell the back-end to notify the front-end that a device has been
414 added or removed.
415 """
416 self.getBackendInterface().interfaceChanged()
418 def attachBackend(self):
419 """Attach the device to its controller.
421 """
422 backend = self.getBackendInterface()
423 d1 = backend.connect()
424 d2 = defer.Deferred()
425 d2.addCallback(self.send_be_vbd_create)
426 d1.chainDeferred(d2)
427 return d2
429 def send_be_vbd_create(self, val):
430 d = defer.Deferred()
431 d.addCallback(self.respond_be_vbd_create)
432 backend = self.getBackendInterface()
433 msg = packMsg('blkif_be_vbd_create_t',
434 { 'domid' : self.controller.dom,
435 'blkif_handle' : backend.handle,
436 'vdevice' : self.vdev,
437 'readonly' : self.readonly() })
438 backend.writeRequest(msg, response=d)
439 return d
441 def respond_be_vbd_create(self, msg):
442 """Response handler for a be_vbd_create message.
443 Tries to grow the vbd.
445 @param msg: message
446 @type msg: xu message
447 """
448 val = unpackMsg('blkif_be_vbd_create_t', msg)
449 d = self.send_be_vbd_grow()
450 d.addCallback(self.respond_be_vbd_grow)
451 return d
453 def send_be_vbd_grow(self):
454 d = defer.Deferred()
455 backend = self.getBackendInterface()
456 msg = packMsg('blkif_be_vbd_grow_t',
457 { 'domid' : self.controller.dom,
458 'blkif_handle' : backend.handle,
459 'vdevice' : self.vdev,
460 'extent.device' : self.device,
461 'extent.sector_start' : self.start_sector,
462 'extent.sector_length' : self.nr_sectors })
463 backend.writeRequest(msg, response=d)
464 return d
466 def respond_be_vbd_grow(self, msg):
467 """Response handler for a be_vbd_grow message.
469 @param msg: message
470 @type msg: xu message
471 """
472 val = unpackMsg('blkif_be_vbd_grow_t', msg)
473 status = val['status']
474 if status != BLKIF_BE_STATUS_OKAY:
475 raise XendError("Adding extent to vbd failed: device %s, error %d"
476 % (sxp.to_string(self.config), status))
477 return self
479 def send_be_vbd_destroy(self):
480 d = defer.Deferred()
481 backend = self.getBackendInterface()
482 msg = packMsg('blkif_be_vbd_destroy_t',
483 { 'domid' : self.controller.dom,
484 'blkif_handle' : backend.handle,
485 'vdevice' : self.vdev })
486 self.controller.delDevice(self.vdev)
487 backend.writeRequest(msg, response=d)
488 return d
491 class BlkifController(controller.SplitController):
492 """Block device interface controller. Handles all block devices
493 for a domain.
494 """
496 def __init__(self, factory, dom):
497 """Create a block device controller.
498 Do not call directly - use createController() on the factory instead.
499 """
500 controller.SplitController.__init__(self, factory, dom)
501 self.addMethod(CMSG_BLKIF_FE,
502 CMSG_BLKIF_FE_DRIVER_STATUS,
503 self.recv_fe_driver_status)
504 self.addMethod(CMSG_BLKIF_FE,
505 CMSG_BLKIF_FE_INTERFACE_CONNECT,
506 self.recv_fe_interface_connect)
507 self.registerChannel()
509 def sxpr(self):
510 val = ['blkif', ['dom', self.dom]]
511 return val
513 def addDevice(self, idx, config):
514 """Add a device to the device table.
516 @param vdev: device index
517 @type vdev: int
518 @param config: device configuration
519 @return: device
520 @rtype: BlkDev
521 """
522 if idx in self.devices:
523 raise XendError('device exists: ' + str(idx))
524 dev = BlkDev(idx, self, config )
525 self.devices[idx] = dev
526 return dev
528 def attachDevice(self, idx, config, recreate=0):
529 """Attach a device to the specified interface.
530 On success the returned deferred will be called with the device.
532 @param idx: device id
533 @param config: device configuration
534 @param recreate: if true it's being recreated (after xend restart)
535 @type recreate: bool
536 @return: deferred
537 @rtype: Deferred
538 """
539 dev = self.addDevice(idx, config)
540 if recreate:
541 dev.recreate(recreate)
542 d = defer.succeed(dev)
543 else:
544 d = dev.attach()
545 return d
547 def destroy(self):
548 """Destroy the controller and all devices.
549 """
550 log.debug("Destroying blkif domain=%d", self.dom)
551 self.destroyDevices()
552 self.destroyBackends()
554 def destroyDevices(self):
555 """Destroy all devices.
556 """
557 for dev in self.getDevices():
558 dev.destroy()
560 def destroyBackends(self):
561 for backend in self.getBackendInterfaces():
562 backend.destroy()
564 def recv_fe_driver_status(self, msg, req):
565 val = unpackMsg('blkif_fe_driver_status_t', msg)
566 print 'recv_fe_driver_status>', val
567 for backend in self.getBackendInterfaces():
568 backend.interfaceDisconnected()
570 def recv_fe_interface_connect(self, msg, req):
571 val = unpackMsg('blkif_fe_interface_connect_t', msg)
572 handle = val['handle']
573 backend = self.getBackendInterfaceByHandle(handle)
574 if backend:
575 backend.connectInterface(val)
576 else:
577 log.error('interface connect on unknown interface: handle=%d', handle)