]> git.proxmox.com Git - mirror_qemu.git/commitdiff
Merge remote-tracking branch 'qemu-kvm/uq/master' into staging
authorAnthony Liguori <anthony@codemonkey.ws>
Mon, 23 Sep 2013 16:52:49 +0000 (11:52 -0500)
committerAnthony Liguori <anthony@codemonkey.ws>
Mon, 23 Sep 2013 16:52:49 +0000 (11:52 -0500)
# By Alexey Kardashevskiy (3) and others
# Via Paolo Bonzini
* qemu-kvm/uq/master:
  target-i386: add feature kvm_pv_unhalt
  linux-headers: update to 3.12-rc1
  target-i386: forward CPUID cache leaves when -cpu host is used
  linux-headers: update to 3.11
  kvm: fix traces to use %x instead of %d
  kvmvapic: Clear also physical ROM address when entering INACTIVE state
  kvmvapic: Enter inactive state on hardware reset
  kvmvapic: Catch invalid ROM size
  kvm irqfd: support direct msimessage to irq translation
  fix steal time MSR vmsd callback to proper opaque type
  kvm: warn if num cpus is greater than num recommended
  cpu: Move cpu state syncs up into cpu_dump_state()
  exec: always use MADV_DONTFORK

Message-id: 1379694292-1601-1-git-send-email-pbonzini@redhat.com

130 files changed:
MAINTAINERS
Makefile
QMP/README [deleted file]
QMP/qemu-ga-client [deleted file]
QMP/qmp [deleted file]
QMP/qmp-events.txt [deleted file]
QMP/qmp-shell [deleted file]
QMP/qmp-spec.txt [deleted file]
QMP/qmp.py [deleted file]
QMP/qom-fuse [deleted file]
QMP/qom-get [deleted file]
QMP/qom-list [deleted file]
QMP/qom-set [deleted file]
block.c
block/blkdebug.c
block/blkverify.c
block/bochs.c
block/cloop.c
block/cow.c
block/curl.c
block/dmg.c
block/gluster.c
block/iscsi.c
block/mirror.c
block/nbd.c
block/parallels.c
block/qcow.c
block/qcow2-cache.c
block/qcow2-cluster.c
block/qcow2-refcount.c
block/qcow2-snapshot.c
block/qcow2.c
block/qcow2.h
block/qed.c
block/raw-posix.c
block/raw-win32.c
block/raw_bsd.c
block/rbd.c
block/sheepdog.c
block/snapshot.c
block/ssh.c
block/vdi.c
block/vhdx.c
block/vmdk.c
block/vpc.c
block/vvfat.c
blockdev.c
configure
docs/qmp/README [new file with mode: 0644]
docs/qmp/qmp-events.txt [new file with mode: 0644]
docs/qmp/qmp-spec.txt [new file with mode: 0644]
exec.c
hmp-commands.hx
hmp.c
hmp.h
hw/block/xen_disk.c
hw/display/Makefile.objs
hw/display/qxl.c
hw/display/qxl.h
hw/i386/pc_sysfw.c
hw/scsi/lsi53c895a.c
hw/scsi/scsi-bus.c
hw/scsi/spapr_vscsi.c
hw/scsi/srp.h
hw/scsi/virtio-scsi.c
hw/usb/combined-packet.c
hw/usb/core.c
hw/usb/hcd-xhci.c
hw/usb/host-bsd.c [deleted file]
hw/usb/host-linux.c [deleted file]
include/block/block.h
include/block/block_int.h
include/block/snapshot.h
include/exec/cpu-all.h
include/exec/exec-all.h
include/hw/pci/pci_ids.h
include/qemu-common.h
include/sysemu/kvm.h
include/sysemu/sysemu.h
kvm-all.c
main-loop.c
qapi-schema.json
qemu-char.c
qemu-coroutine.c
qemu-img-cmds.hx
qemu-img.c
qemu-img.texi
qemu-io.c
qemu-nbd.c
qmp-commands.hx
savevm.c
scripts/qmp/qemu-ga-client [new file with mode: 0755]
scripts/qmp/qmp [new file with mode: 0755]
scripts/qmp/qmp-shell [new file with mode: 0755]
scripts/qmp/qmp.py [new file with mode: 0644]
scripts/qmp/qom-fuse [new file with mode: 0755]
scripts/qmp/qom-get [new file with mode: 0755]
scripts/qmp/qom-list [new file with mode: 0755]
scripts/qmp/qom-set [new file with mode: 0755]
slirp/libslirp.h
slirp/slirp.c
slirp/slirp.h
stubs/Makefile.objs
stubs/slirp.c
stubs/uuid.c [new file with mode: 0644]
target-i386/helper.c
target-i386/translate.c
target-s390x/kvm.c
tests/Makefile
tests/qemu-iotests/007
tests/qemu-iotests/038
tests/qemu-iotests/038.out
tests/qemu-iotests/045
tests/qemu-iotests/045.out
tests/qemu-iotests/049.out
tests/qemu-iotests/051.out
tests/qemu-iotests/054.out
tests/qemu-iotests/057 [new file with mode: 0755]
tests/qemu-iotests/057.out [new file with mode: 0644]
tests/qemu-iotests/060
tests/qemu-iotests/060.out
tests/qemu-iotests/061 [new file with mode: 0755]
tests/qemu-iotests/061.out [new file with mode: 0644]
tests/qemu-iotests/check
tests/qemu-iotests/common.rc
tests/qemu-iotests/group
tests/qemu-iotests/iotests.py
tests/qemu-iotests/socket_scm_helper.c [new file with mode: 0644]
util/oslib-posix.c
util/oslib-win32.c

index d128ed035a4212bb7c0e625c62bec0cd2d70fd06..09c5148cdd7429c4870361105ba1c79f465b792b 100644 (file)
@@ -766,6 +766,12 @@ M: Blue Swirl <blauwirbel@gmail.com>
 S: Odd Fixes
 F: scripts/checkpatch.pl
 
+Seccomp
+M: Eduardo Otubo <otubo@linux.vnet.ibm.com>
+S: Supported
+F: qemu-seccomp.c
+F: include/sysemu/seccomp.h
+
 Usermode Emulation
 ------------------
 BSD user
index 362fe3e66e3998260eb6bb561745a2f8a19a7ae4..aca548dcdf640c4353ac7c0b81949d6c8579892d 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -65,7 +65,7 @@ LIBS+=-lz $(LIBS_TOOLS)
 HELPERS-$(CONFIG_LINUX) = qemu-bridge-helper$(EXESUF)
 
 ifdef BUILD_DOCS
-DOCS=qemu-doc.html qemu-tech.html qemu.1 qemu-img.1 qemu-nbd.8 QMP/qmp-commands.txt
+DOCS=qemu-doc.html qemu-tech.html qemu.1 qemu-img.1 qemu-nbd.8 qmp-commands.txt
 ifdef CONFIG_VIRTFS
 DOCS+=fsdev/virtfs-proxy-helper.1
 endif
@@ -304,7 +304,7 @@ endif
 install-doc: $(DOCS)
        $(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)"
        $(INSTALL_DATA) qemu-doc.html  qemu-tech.html "$(DESTDIR)$(qemu_docdir)"
-       $(INSTALL_DATA) QMP/qmp-commands.txt "$(DESTDIR)$(qemu_docdir)"
+       $(INSTALL_DATA) qmp-commands.txt "$(DESTDIR)$(qemu_docdir)"
 ifdef CONFIG_POSIX
        $(INSTALL_DIR) "$(DESTDIR)$(mandir)/man1"
        $(INSTALL_DATA) qemu.1 "$(DESTDIR)$(mandir)/man1"
@@ -398,7 +398,7 @@ qemu-options.texi: $(SRC_PATH)/qemu-options.hx
 qemu-monitor.texi: $(SRC_PATH)/hmp-commands.hx
        $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"  GEN   $@")
 
-QMP/qmp-commands.txt: $(SRC_PATH)/qmp-commands.hx
+qmp-commands.txt: $(SRC_PATH)/qmp-commands.hx
        $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -q < $< > $@,"  GEN   $@")
 
 qemu-img-cmds.texi: $(SRC_PATH)/qemu-img-cmds.hx
diff --git a/QMP/README b/QMP/README
deleted file mode 100644 (file)
index c95a08c..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-                          QEMU Monitor Protocol
-                          =====================
-
-Introduction
--------------
-
-The QEMU Monitor Protocol (QMP) allows applications to communicate with
-QEMU's Monitor.
-
-QMP is JSON[1] based and currently has the following features:
-
-- Lightweight, text-based, easy to parse data format
-- Asynchronous messages support (ie. events)
-- Capabilities Negotiation
-
-For detailed information on QMP's usage, please, refer to the following files:
-
-o qmp-spec.txt      QEMU Monitor Protocol current specification
-o qmp-commands.txt  QMP supported commands (auto-generated at build-time)
-o qmp-events.txt    List of available asynchronous events
-
-There is also a simple Python script called 'qmp-shell' available.
-
-IMPORTANT: It's strongly recommended to read the 'Stability Considerations'
-section in the qmp-commands.txt file before making any serious use of QMP.
-
-
-[1] http://www.json.org
-
-Usage
------
-
-To enable QMP, you need a QEMU monitor instance in "control mode". There are
-two ways of doing this.
-
-The simplest one is using the '-qmp' command-line option. The following
-example makes QMP available on localhost port 4444:
-
-  $ qemu [...] -qmp tcp:localhost:4444,server
-
-However, in order to have more complex combinations, like multiple monitors,
-the '-mon' command-line option should be used along with the '-chardev' one.
-For instance, the following example creates one user monitor on stdio and one
-QMP monitor on localhost port 4444.
-
-   $ qemu [...] -chardev stdio,id=mon0 -mon chardev=mon0,mode=readline \
-                -chardev socket,id=mon1,host=localhost,port=4444,server \
-                -mon chardev=mon1,mode=control
-
-Please, refer to QEMU's manpage for more information.
-
-Simple Testing
---------------
-
-To manually test QMP one can connect with telnet and issue commands by hand:
-
-$ telnet localhost 4444
-Trying 127.0.0.1...
-Connected to localhost.
-Escape character is '^]'.
-{"QMP": {"version": {"qemu": {"micro": 50, "minor": 13, "major": 0}, "package": ""}, "capabilities": []}}
-{ "execute": "qmp_capabilities" }
-{"return": {}}
-{ "execute": "query-version" }
-{"return": {"qemu": {"micro": 50, "minor": 13, "major": 0}, "package": ""}}
-
-Development Process
--------------------
-
-When changing QMP's interface (by adding new commands, events or modifying
-existing ones) it's mandatory to update the relevant documentation, which is
-one (or more) of the files listed in the 'Introduction' section*.
-
-Also, it's strongly recommended to send the documentation patch first, before
-doing any code change. This is so because:
-
-  1. Avoids the code dictating the interface
-
-  2. Review can improve your interface.  Letting that happen before
-     you implement it can save you work.
-
-* The qmp-commands.txt file is generated from the qmp-commands.hx one, which
-  is the file that should be edited.
-
-Homepage
---------
-
-http://wiki.qemu.org/QMP
diff --git a/QMP/qemu-ga-client b/QMP/qemu-ga-client
deleted file mode 100755 (executable)
index b5f7e7c..0000000
+++ /dev/null
@@ -1,301 +0,0 @@
-#!/usr/bin/python
-
-# QEMU Guest Agent Client
-#
-# Copyright (C) 2012 Ryota Ozaki <ozaki.ryota@gmail.com>
-#
-# This work is licensed under the terms of the GNU GPL, version 2.  See
-# the COPYING file in the top-level directory.
-#
-# Usage:
-#
-# Start QEMU with:
-#
-# # qemu [...] -chardev socket,path=/tmp/qga.sock,server,nowait,id=qga0 \
-#   -device virtio-serial -device virtserialport,chardev=qga0,name=org.qemu.guest_agent.0
-#
-# Run the script:
-#
-# $ qemu-ga-client --address=/tmp/qga.sock <command> [args...]
-#
-# or
-#
-# $ export QGA_CLIENT_ADDRESS=/tmp/qga.sock
-# $ qemu-ga-client <command> [args...]
-#
-# For example:
-#
-# $ qemu-ga-client cat /etc/resolv.conf
-# # Generated by NetworkManager
-# nameserver 10.0.2.3
-# $ qemu-ga-client fsfreeze status
-# thawed
-# $ qemu-ga-client fsfreeze freeze
-# 2 filesystems frozen
-#
-# See also: http://wiki.qemu.org/Features/QAPI/GuestAgent
-#
-
-import base64
-import random
-
-import qmp
-
-
-class QemuGuestAgent(qmp.QEMUMonitorProtocol):
-    def __getattr__(self, name):
-        def wrapper(**kwds):
-            return self.command('guest-' + name.replace('_', '-'), **kwds)
-        return wrapper
-
-
-class QemuGuestAgentClient:
-    error = QemuGuestAgent.error
-
-    def __init__(self, address):
-        self.qga = QemuGuestAgent(address)
-        self.qga.connect(negotiate=False)
-
-    def sync(self, timeout=3):
-        # Avoid being blocked forever
-        if not self.ping(timeout):
-            raise EnvironmentError('Agent seems not alive')
-        uid = random.randint(0, (1 << 32) - 1)
-        while True:
-            ret = self.qga.sync(id=uid)
-            if isinstance(ret, int) and int(ret) == uid:
-                break
-
-    def __file_read_all(self, handle):
-        eof = False
-        data = ''
-        while not eof:
-            ret = self.qga.file_read(handle=handle, count=1024)
-            _data = base64.b64decode(ret['buf-b64'])
-            data += _data
-            eof = ret['eof']
-        return data
-
-    def read(self, path):
-        handle = self.qga.file_open(path=path)
-        try:
-            data = self.__file_read_all(handle)
-        finally:
-            self.qga.file_close(handle=handle)
-        return data
-
-    def info(self):
-        info = self.qga.info()
-
-        msgs = []
-        msgs.append('version: ' + info['version'])
-        msgs.append('supported_commands:')
-        enabled = [c['name'] for c in info['supported_commands'] if c['enabled']]
-        msgs.append('\tenabled: ' + ', '.join(enabled))
-        disabled = [c['name'] for c in info['supported_commands'] if not c['enabled']]
-        msgs.append('\tdisabled: ' + ', '.join(disabled))
-
-        return '\n'.join(msgs)
-
-    def __gen_ipv4_netmask(self, prefixlen):
-        mask = int('1' * prefixlen + '0' * (32 - prefixlen), 2)
-        return '.'.join([str(mask >> 24),
-                         str((mask >> 16) & 0xff),
-                         str((mask >> 8) & 0xff),
-                         str(mask & 0xff)])
-
-    def ifconfig(self):
-        nifs = self.qga.network_get_interfaces()
-
-        msgs = []
-        for nif in nifs:
-            msgs.append(nif['name'] + ':')
-            if 'ip-addresses' in nif:
-                for ipaddr in nif['ip-addresses']:
-                    if ipaddr['ip-address-type'] == 'ipv4':
-                        addr = ipaddr['ip-address']
-                        mask = self.__gen_ipv4_netmask(int(ipaddr['prefix']))
-                        msgs.append("\tinet %s  netmask %s" % (addr, mask))
-                    elif ipaddr['ip-address-type'] == 'ipv6':
-                        addr = ipaddr['ip-address']
-                        prefix = ipaddr['prefix']
-                        msgs.append("\tinet6 %s  prefixlen %s" % (addr, prefix))
-            if nif['hardware-address'] != '00:00:00:00:00:00':
-                msgs.append("\tether " + nif['hardware-address'])
-
-        return '\n'.join(msgs)
-
-    def ping(self, timeout):
-        self.qga.settimeout(timeout)
-        try:
-            self.qga.ping()
-        except self.qga.timeout:
-            return False
-        return True
-
-    def fsfreeze(self, cmd):
-        if cmd not in ['status', 'freeze', 'thaw']:
-            raise StandardError('Invalid command: ' + cmd)
-
-        return getattr(self.qga, 'fsfreeze' + '_' + cmd)()
-
-    def fstrim(self, minimum=0):
-        return getattr(self.qga, 'fstrim')(minimum=minimum)
-
-    def suspend(self, mode):
-        if mode not in ['disk', 'ram', 'hybrid']:
-            raise StandardError('Invalid mode: ' + mode)
-
-        try:
-            getattr(self.qga, 'suspend' + '_' + mode)()
-            # On error exception will raise
-        except self.qga.timeout:
-            # On success command will timed out
-            return
-
-    def shutdown(self, mode='powerdown'):
-        if mode not in ['powerdown', 'halt', 'reboot']:
-            raise StandardError('Invalid mode: ' + mode)
-
-        try:
-            self.qga.shutdown(mode=mode)
-        except self.qga.timeout:
-            return
-
-
-def _cmd_cat(client, args):
-    if len(args) != 1:
-        print('Invalid argument')
-        print('Usage: cat <file>')
-        sys.exit(1)
-    print(client.read(args[0]))
-
-
-def _cmd_fsfreeze(client, args):
-    usage = 'Usage: fsfreeze status|freeze|thaw'
-    if len(args) != 1:
-        print('Invalid argument')
-        print(usage)
-        sys.exit(1)
-    if args[0] not in ['status', 'freeze', 'thaw']:
-        print('Invalid command: ' + args[0])
-        print(usage)
-        sys.exit(1)
-    cmd = args[0]
-    ret = client.fsfreeze(cmd)
-    if cmd == 'status':
-        print(ret)
-    elif cmd == 'freeze':
-        print("%d filesystems frozen" % ret)
-    else:
-        print("%d filesystems thawed" % ret)
-
-
-def _cmd_fstrim(client, args):
-    if len(args) == 0:
-        minimum = 0
-    else:
-        minimum = int(args[0])
-    print(client.fstrim(minimum))
-
-
-def _cmd_ifconfig(client, args):
-    print(client.ifconfig())
-
-
-def _cmd_info(client, args):
-    print(client.info())
-
-
-def _cmd_ping(client, args):
-    if len(args) == 0:
-        timeout = 3
-    else:
-        timeout = float(args[0])
-    alive = client.ping(timeout)
-    if not alive:
-        print("Not responded in %s sec" % args[0])
-        sys.exit(1)
-
-
-def _cmd_suspend(client, args):
-    usage = 'Usage: suspend disk|ram|hybrid'
-    if len(args) != 1:
-        print('Less argument')
-        print(usage)
-        sys.exit(1)
-    if args[0] not in ['disk', 'ram', 'hybrid']:
-        print('Invalid command: ' + args[0])
-        print(usage)
-        sys.exit(1)
-    client.suspend(args[0])
-
-
-def _cmd_shutdown(client, args):
-    client.shutdown()
-_cmd_powerdown = _cmd_shutdown
-
-
-def _cmd_halt(client, args):
-    client.shutdown('halt')
-
-
-def _cmd_reboot(client, args):
-    client.shutdown('reboot')
-
-
-commands = [m.replace('_cmd_', '') for m in dir() if '_cmd_' in m]
-
-
-def main(address, cmd, args):
-    if not os.path.exists(address):
-        print('%s not found' % address)
-        sys.exit(1)
-
-    if cmd not in commands:
-        print('Invalid command: ' + cmd)
-        print('Available commands: ' + ', '.join(commands))
-        sys.exit(1)
-
-    try:
-        client = QemuGuestAgentClient(address)
-    except QemuGuestAgent.error, e:
-        import errno
-
-        print(e)
-        if e.errno == errno.ECONNREFUSED:
-            print('Hint: qemu is not running?')
-        sys.exit(1)
-
-    if cmd == 'fsfreeze' and args[0] == 'freeze':
-        client.sync(60)
-    elif cmd != 'ping':
-        client.sync()
-
-    globals()['_cmd_' + cmd](client, args)
-
-
-if __name__ == '__main__':
-    import sys
-    import os
-    import optparse
-
-    address = os.environ['QGA_CLIENT_ADDRESS'] if 'QGA_CLIENT_ADDRESS' in os.environ else None
-
-    usage = "%prog [--address=<unix_path>|<ipv4_address>] <command> [args...]\n"
-    usage += '<command>: ' + ', '.join(commands)
-    parser = optparse.OptionParser(usage=usage)
-    parser.add_option('--address', action='store', type='string',
-                      default=address, help='Specify a ip:port pair or a unix socket path')
-    options, args = parser.parse_args()
-
-    address = options.address
-    if address is None:
-        parser.error('address is not specified')
-        sys.exit(1)
-
-    if len(args) == 0:
-        parser.error('Less argument')
-        sys.exit(1)
-
-    main(address, args[0], args[1:])
diff --git a/QMP/qmp b/QMP/qmp
deleted file mode 100755 (executable)
index 1db3c7f..0000000
--- a/QMP/qmp
+++ /dev/null
@@ -1,126 +0,0 @@
-#!/usr/bin/python
-#
-# QMP command line tool
-#
-# Copyright IBM, Corp. 2011
-#
-# Authors:
-#  Anthony Liguori <aliguori@us.ibm.com>
-#
-# This work is licensed under the terms of the GNU GPLv2 or later.
-# See the COPYING file in the top-level directory.
-
-import sys, os
-from qmp import QEMUMonitorProtocol
-
-def print_response(rsp, prefix=[]):
-    if type(rsp) == list:
-        i = 0
-        for item in rsp:
-            if prefix == []:
-                prefix = ['item']
-            print_response(item, prefix[:-1] + ['%s[%d]' % (prefix[-1], i)])
-            i += 1
-    elif type(rsp) == dict:
-        for key in rsp.keys():
-            print_response(rsp[key], prefix + [key])
-    else:
-        if len(prefix):
-            print '%s: %s' % ('.'.join(prefix), rsp)
-        else:
-            print '%s' % (rsp)
-
-def main(args):
-    path = None
-
-    # Use QMP_PATH if it's set
-    if os.environ.has_key('QMP_PATH'):
-        path = os.environ['QMP_PATH']
-
-    while len(args):
-        arg = args[0]
-
-        if arg.startswith('--'):
-            arg = arg[2:]
-            if arg.find('=') == -1:
-                value = True
-            else:
-                arg, value = arg.split('=', 1)
-
-            if arg in ['path']:
-                if type(value) == str:
-                    path = value
-            elif arg in ['help']:
-                os.execlp('man', 'man', 'qmp')
-            else:
-                print 'Unknown argument "%s"' % arg
-
-            args = args[1:]
-        else:
-            break
-
-    if not path:
-        print "QMP path isn't set, use --path=qmp-monitor-address or set QMP_PATH"
-        return 1
-
-    if len(args):
-        command, args = args[0], args[1:]
-    else:
-        print 'No command found'
-        print 'Usage: "qmp [--path=qmp-monitor-address] qmp-cmd arguments"'
-        return 1
-
-    if command in ['help']:
-        os.execlp('man', 'man', 'qmp')
-
-    srv = QEMUMonitorProtocol(path)
-    srv.connect()
-
-    def do_command(srv, cmd, **kwds):
-        rsp = srv.cmd(cmd, kwds)
-        if rsp.has_key('error'):
-            raise Exception(rsp['error']['desc'])
-        return rsp['return']
-
-    commands = map(lambda x: x['name'], do_command(srv, 'query-commands'))
-
-    srv.close()
-
-    if command not in commands:
-        fullcmd = 'qmp-%s' % command
-        try:
-            os.environ['QMP_PATH'] = path
-            os.execvp(fullcmd, [fullcmd] + args)
-        except OSError, (errno, msg):
-            if errno == 2:
-                print 'Command "%s" not found.' % (fullcmd)
-                return 1
-            raise
-        return 0
-
-    srv = QEMUMonitorProtocol(path)
-    srv.connect()
-
-    arguments = {}
-    for arg in args:
-        if not arg.startswith('--'):
-            print 'Unknown argument "%s"' % arg
-            return 1
-
-        arg = arg[2:]
-        if arg.find('=') == -1:
-            value = True
-        else:
-            arg, value = arg.split('=', 1)
-
-        if arg in ['help']:
-            os.execlp('man', 'man', 'qmp-%s' % command)
-            return 1
-
-        arguments[arg] = value
-
-    rsp = do_command(srv, command, **arguments)
-    print_response(rsp)
-
-if __name__ == '__main__':
-    sys.exit(main(sys.argv[1:]))
diff --git a/QMP/qmp-events.txt b/QMP/qmp-events.txt
deleted file mode 100644 (file)
index 4b24ec9..0000000
+++ /dev/null
@@ -1,502 +0,0 @@
-                   QEMU Monitor Protocol Events
-                   ============================
-
-BALLOON_CHANGE
---------------
-
-Emitted when the guest changes the actual BALLOON level. This
-value is equivalent to the 'actual' field return by the
-'query-balloon' command
-
-Data:
-
-- "actual": actual level of the guest memory balloon in bytes (json-number)
-
-Example:
-
-{ "event": "BALLOON_CHANGE",
-    "data": { "actual": 944766976 },
-    "timestamp": { "seconds": 1267020223, "microseconds": 435656 } }
-
-BLOCK_IMAGE_CORRUPTED
----------------------
-
-Emitted when a disk image is being marked corrupt.
-
-Data:
-
-- "device": Device name (json-string)
-- "msg":    Informative message (e.g., reason for the corruption) (json-string)
-- "offset": If the corruption resulted from an image access, this is the access
-            offset into the image (json-int)
-- "size":   If the corruption resulted from an image access, this is the access
-            size (json-int)
-
-Example:
-
-{ "event": "BLOCK_IMAGE_CORRUPTED",
-    "data": { "device": "ide0-hd0",
-        "msg": "Prevented active L1 table overwrite", "offset": 196608,
-        "size": 65536 },
-    "timestamp": { "seconds": 1378126126, "microseconds": 966463 } }
-
-BLOCK_IO_ERROR
---------------
-
-Emitted when a disk I/O error occurs.
-
-Data:
-
-- "device": device name (json-string)
-- "operation": I/O operation (json-string, "read" or "write")
-- "action": action that has been taken, it's one of the following (json-string):
-    "ignore": error has been ignored
-    "report": error has been reported to the device
-    "stop": error caused VM to be stopped
-
-Example:
-
-{ "event": "BLOCK_IO_ERROR",
-    "data": { "device": "ide0-hd1",
-              "operation": "write",
-              "action": "stop" },
-    "timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
-
-Note: If action is "stop", a STOP event will eventually follow the
-BLOCK_IO_ERROR event.
-
-BLOCK_JOB_CANCELLED
--------------------
-
-Emitted when a block job has been cancelled.
-
-Data:
-
-- "type":     Job type (json-string; "stream" for image streaming
-                                     "commit" for block commit)
-- "device":   Device name (json-string)
-- "len":      Maximum progress value (json-int)
-- "offset":   Current progress value (json-int)
-              On success this is equal to len.
-              On failure this is less than len.
-- "speed":    Rate limit, bytes per second (json-int)
-
-Example:
-
-{ "event": "BLOCK_JOB_CANCELLED",
-     "data": { "type": "stream", "device": "virtio-disk0",
-               "len": 10737418240, "offset": 134217728,
-               "speed": 0 },
-     "timestamp": { "seconds": 1267061043, "microseconds": 959568 } }
-
-BLOCK_JOB_COMPLETED
--------------------
-
-Emitted when a block job has completed.
-
-Data:
-
-- "type":     Job type (json-string; "stream" for image streaming
-                                     "commit" for block commit)
-- "device":   Device name (json-string)
-- "len":      Maximum progress value (json-int)
-- "offset":   Current progress value (json-int)
-              On success this is equal to len.
-              On failure this is less than len.
-- "speed":    Rate limit, bytes per second (json-int)
-- "error":    Error message (json-string, optional)
-              Only present on failure.  This field contains a human-readable
-              error message.  There are no semantics other than that streaming
-              has failed and clients should not try to interpret the error
-              string.
-
-Example:
-
-{ "event": "BLOCK_JOB_COMPLETED",
-     "data": { "type": "stream", "device": "virtio-disk0",
-               "len": 10737418240, "offset": 10737418240,
-               "speed": 0 },
-     "timestamp": { "seconds": 1267061043, "microseconds": 959568 } }
-
-BLOCK_JOB_ERROR
----------------
-
-Emitted when a block job encounters an error.
-
-Data:
-
-- "device": device name (json-string)
-- "operation": I/O operation (json-string, "read" or "write")
-- "action": action that has been taken, it's one of the following (json-string):
-    "ignore": error has been ignored, the job may fail later
-    "report": error will be reported and the job canceled
-    "stop": error caused job to be paused
-
-Example:
-
-{ "event": "BLOCK_JOB_ERROR",
-    "data": { "device": "ide0-hd1",
-              "operation": "write",
-              "action": "stop" },
-    "timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
-
-BLOCK_JOB_READY
----------------
-
-Emitted when a block job is ready to complete.
-
-Data:
-
-- "device": device name (json-string)
-
-Example:
-
-{ "event": "BLOCK_JOB_READY",
-    "data": { "device": "ide0-hd1" },
-    "timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
-
-Note: The "ready to complete" status is always reset by a BLOCK_JOB_ERROR
-event.
-
-DEVICE_DELETED
------------------
-
-Emitted whenever the device removal completion is acknowledged
-by the guest.
-At this point, it's safe to reuse the specified device ID.
-Device removal can be initiated by the guest or by HMP/QMP commands.
-
-Data:
-
-- "device": device name (json-string, optional)
-- "path": device path (json-string)
-
-{ "event": "DEVICE_DELETED",
-  "data": { "device": "virtio-net-pci-0",
-            "path": "/machine/peripheral/virtio-net-pci-0" },
-  "timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
-
-DEVICE_TRAY_MOVED
------------------
-
-It's emitted whenever the tray of a removable device is moved by the guest
-or by HMP/QMP commands.
-
-Data:
-
-- "device": device name (json-string)
-- "tray-open": true if the tray has been opened or false if it has been closed
-               (json-bool)
-
-{ "event": "DEVICE_TRAY_MOVED",
-  "data": { "device": "ide1-cd0",
-            "tray-open": true
-  },
-  "timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
-
-NIC_RX_FILTER_CHANGED
------------------
-
-The event is emitted once until the query command is executed,
-the first event will always be emitted.
-
-Data:
-
-- "name": net client name (json-string)
-- "path": device path (json-string)
-
-{ "event": "NIC_RX_FILTER_CHANGED",
-  "data": { "name": "vnet0",
-            "path": "/machine/peripheral/vnet0/virtio-backend" },
-  "timestamp": { "seconds": 1368697518, "microseconds": 326866 } }
-}
-
-RESET
------
-
-Emitted when the Virtual Machine is reseted.
-
-Data: None.
-
-Example:
-
-{ "event": "RESET",
-    "timestamp": { "seconds": 1267041653, "microseconds": 9518 } }
-
-RESUME
-------
-
-Emitted when the Virtual Machine resumes execution.
-
-Data: None.
-
-Example:
-
-{ "event": "RESUME",
-    "timestamp": { "seconds": 1271770767, "microseconds": 582542 } }
-
-RTC_CHANGE
-----------
-
-Emitted when the guest changes the RTC time.
-
-Data:
-
-- "offset": Offset between base RTC clock (as specified by -rtc base), and
-new RTC clock value (json-number)
-
-Example:
-
-{ "event": "RTC_CHANGE",
-    "data": { "offset": 78 },
-    "timestamp": { "seconds": 1267020223, "microseconds": 435656 } }
-
-SHUTDOWN
---------
-
-Emitted when the Virtual Machine is powered down.
-
-Data: None.
-
-Example:
-
-{ "event": "SHUTDOWN",
-    "timestamp": { "seconds": 1267040730, "microseconds": 682951 } }
-
-Note: If the command-line option "-no-shutdown" has been specified, a STOP
-event will eventually follow the SHUTDOWN event.
-
-SPICE_CONNECTED, SPICE_DISCONNECTED
------------------------------------
-
-Emitted when a SPICE client connects or disconnects.
-
-Data:
-
-- "server": Server information (json-object)
-  - "host": IP address (json-string)
-  - "port": port number (json-string)
-  - "family": address family (json-string, "ipv4" or "ipv6")
-- "client": Client information (json-object)
-  - "host": IP address (json-string)
-  - "port": port number (json-string)
-  - "family": address family (json-string, "ipv4" or "ipv6")
-
-Example:
-
-{ "timestamp": {"seconds": 1290688046, "microseconds": 388707},
-  "event": "SPICE_CONNECTED",
-  "data": {
-    "server": { "port": "5920", "family": "ipv4", "host": "127.0.0.1"},
-    "client": {"port": "52873", "family": "ipv4", "host": "127.0.0.1"}
-}}
-
-SPICE_INITIALIZED
------------------
-
-Emitted after initial handshake and authentication takes place (if any)
-and the SPICE channel is up'n'running
-
-Data:
-
-- "server": Server information (json-object)
-  - "host": IP address (json-string)
-  - "port": port number (json-string)
-  - "family": address family (json-string, "ipv4" or "ipv6")
-  - "auth": authentication method (json-string, optional)
-- "client": Client information (json-object)
-  - "host": IP address (json-string)
-  - "port": port number (json-string)
-  - "family": address family (json-string, "ipv4" or "ipv6")
-  - "connection-id": spice connection id.  All channels with the same id
-                     belong to the same spice session (json-int)
-  - "channel-type": channel type.  "1" is the main control channel, filter for
-                    this one if you want track spice sessions only (json-int)
-  - "channel-id": channel id.  Usually "0", might be different needed when
-                  multiple channels of the same type exist, such as multiple
-                  display channels in a multihead setup (json-int)
-  - "tls": whevener the channel is encrypted (json-bool)
-
-Example:
-
-{ "timestamp": {"seconds": 1290688046, "microseconds": 417172},
-  "event": "SPICE_INITIALIZED",
-  "data": {"server": {"auth": "spice", "port": "5921",
-                      "family": "ipv4", "host": "127.0.0.1"},
-           "client": {"port": "49004", "family": "ipv4", "channel-type": 3,
-                      "connection-id": 1804289383, "host": "127.0.0.1",
-                      "channel-id": 0, "tls": true}
-}}
-
-STOP
-----
-
-Emitted when the Virtual Machine is stopped.
-
-Data: None.
-
-Example:
-
-{ "event": "STOP",
-    "timestamp": { "seconds": 1267041730, "microseconds": 281295 } }
-
-SUSPEND
--------
-
-Emitted when guest enters S3 state.
-
-Data: None.
-
-Example:
-
-{ "event": "SUSPEND",
-     "timestamp": { "seconds": 1344456160, "microseconds": 309119 } }
-
-SUSPEND_DISK
-------------
-
-Emitted when the guest makes a request to enter S4 state.
-
-Data: None.
-
-Example:
-
-{ "event": "SUSPEND_DISK",
-     "timestamp": { "seconds": 1344456160, "microseconds": 309119 } }
-
-Note: QEMU shuts down when entering S4 state.
-
-VNC_CONNECTED
--------------
-
-Emitted when a VNC client establishes a connection.
-
-Data:
-
-- "server": Server information (json-object)
-  - "host": IP address (json-string)
-  - "service": port number (json-string)
-  - "family": address family (json-string, "ipv4" or "ipv6")
-  - "auth": authentication method (json-string, optional)
-- "client": Client information (json-object)
-  - "host": IP address (json-string)
-  - "service": port number (json-string)
-  - "family": address family (json-string, "ipv4" or "ipv6")
-
-Example:
-
-{ "event": "VNC_CONNECTED",
-    "data": {
-        "server": { "auth": "sasl", "family": "ipv4",
-                    "service": "5901", "host": "0.0.0.0" },
-        "client": { "family": "ipv4", "service": "58425",
-                    "host": "127.0.0.1" } },
-    "timestamp": { "seconds": 1262976601, "microseconds": 975795 } }
-
-
-Note: This event is emitted before any authentication takes place, thus
-the authentication ID is not provided.
-
-VNC_DISCONNECTED
-----------------
-
-Emitted when the connection is closed.
-
-Data:
-
-- "server": Server information (json-object)
-  - "host": IP address (json-string)
-  - "service": port number (json-string)
-  - "family": address family (json-string, "ipv4" or "ipv6")
-  - "auth": authentication method (json-string, optional)
-- "client": Client information (json-object)
-  - "host": IP address (json-string)
-  - "service": port number (json-string)
-  - "family": address family (json-string, "ipv4" or "ipv6")
-  - "x509_dname": TLS dname (json-string, optional)
-  - "sasl_username": SASL username (json-string, optional)
-
-Example:
-
-{ "event": "VNC_DISCONNECTED",
-    "data": {
-        "server": { "auth": "sasl", "family": "ipv4",
-                    "service": "5901", "host": "0.0.0.0" },
-        "client": { "family": "ipv4", "service": "58425",
-                    "host": "127.0.0.1", "sasl_username": "luiz" } },
-    "timestamp": { "seconds": 1262976601, "microseconds": 975795 } }
-
-VNC_INITIALIZED
----------------
-
-Emitted after authentication takes place (if any) and the VNC session is
-made active.
-
-Data:
-
-- "server": Server information (json-object)
-  - "host": IP address (json-string)
-  - "service": port number (json-string)
-  - "family": address family (json-string, "ipv4" or "ipv6")
-  - "auth": authentication method (json-string, optional)
-- "client": Client information (json-object)
-  - "host": IP address (json-string)
-  - "service": port number (json-string)
-  - "family": address family (json-string, "ipv4" or "ipv6")
-  - "x509_dname": TLS dname (json-string, optional)
-  - "sasl_username": SASL username (json-string, optional)
-
-Example:
-
-{ "event": "VNC_INITIALIZED",
-    "data": {
-        "server": { "auth": "sasl", "family": "ipv4",
-                    "service": "5901", "host": "0.0.0.0"},
-        "client": { "family": "ipv4", "service": "46089",
-                    "host": "127.0.0.1", "sasl_username": "luiz" } },
-        "timestamp": { "seconds": 1263475302, "microseconds": 150772 } }
-
-WAKEUP
-------
-
-Emitted when the guest has woken up from S3 and is running.
-
-Data: None.
-
-Example:
-
-{ "event": "WATCHDOG",
-     "timestamp": { "seconds": 1344522075, "microseconds": 745528 } }
-
-WATCHDOG
---------
-
-Emitted when the watchdog device's timer is expired.
-
-Data:
-
-- "action": Action that has been taken, it's one of the following (json-string):
-            "reset", "shutdown", "poweroff", "pause", "debug", or "none"
-
-Example:
-
-{ "event": "WATCHDOG",
-     "data": { "action": "reset" },
-     "timestamp": { "seconds": 1267061043, "microseconds": 959568 } }
-
-Note: If action is "reset", "shutdown", or "pause" the WATCHDOG event is
-followed respectively by the RESET, SHUTDOWN, or STOP events.
-
-GUEST_PANICKED
---------------
-
-Emitted when guest OS panic is detected.
-
-Data:
-
-- "action": Action that has been taken (json-string, currently always "pause").
-
-Example:
-
-{ "event": "GUEST_PANICKED",
-     "data": { "action": "pause" } }
diff --git a/QMP/qmp-shell b/QMP/qmp-shell
deleted file mode 100755 (executable)
index 73cb3b6..0000000
+++ /dev/null
@@ -1,286 +0,0 @@
-#!/usr/bin/python
-#
-# Low-level QEMU shell on top of QMP.
-#
-# Copyright (C) 2009, 2010 Red Hat Inc.
-#
-# Authors:
-#  Luiz Capitulino <lcapitulino@redhat.com>
-#
-# This work is licensed under the terms of the GNU GPL, version 2.  See
-# the COPYING file in the top-level directory.
-#
-# Usage:
-#
-# Start QEMU with:
-#
-# # qemu [...] -qmp unix:./qmp-sock,server
-#
-# Run the shell:
-#
-# $ qmp-shell ./qmp-sock
-#
-# Commands have the following format:
-#
-#    < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ]
-#
-# For example:
-#
-# (QEMU) device_add driver=e1000 id=net1
-# {u'return': {}}
-# (QEMU)
-
-import qmp
-import readline
-import sys
-import pprint
-
-class QMPCompleter(list):
-    def complete(self, text, state):
-        for cmd in self:
-            if cmd.startswith(text):
-                if not state:
-                    return cmd
-                else:
-                    state -= 1
-
-class QMPShellError(Exception):
-    pass
-
-class QMPShellBadPort(QMPShellError):
-    pass
-
-# TODO: QMPShell's interface is a bit ugly (eg. _fill_completion() and
-#       _execute_cmd()). Let's design a better one.
-class QMPShell(qmp.QEMUMonitorProtocol):
-    def __init__(self, address, pp=None):
-        qmp.QEMUMonitorProtocol.__init__(self, self.__get_address(address))
-        self._greeting = None
-        self._completer = None
-        self._pp = pp
-
-    def __get_address(self, arg):
-        """
-        Figure out if the argument is in the port:host form, if it's not it's
-        probably a file path.
-        """
-        addr = arg.split(':')
-        if len(addr) == 2:
-            try:
-                port = int(addr[1])
-            except ValueError:
-                raise QMPShellBadPort
-            return ( addr[0], port )
-        # socket path
-        return arg
-
-    def _fill_completion(self):
-        for cmd in self.cmd('query-commands')['return']:
-            self._completer.append(cmd['name'])
-
-    def __completer_setup(self):
-        self._completer = QMPCompleter()
-        self._fill_completion()
-        readline.set_completer(self._completer.complete)
-        readline.parse_and_bind("tab: complete")
-        # XXX: default delimiters conflict with some command names (eg. query-),
-        # clearing everything as it doesn't seem to matter
-        readline.set_completer_delims('')
-
-    def __build_cmd(self, cmdline):
-        """
-        Build a QMP input object from a user provided command-line in the
-        following format:
-    
-            < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ]
-        """
-        cmdargs = cmdline.split()
-        qmpcmd = { 'execute': cmdargs[0], 'arguments': {} }
-        for arg in cmdargs[1:]:
-            opt = arg.split('=')
-            try:
-                if(len(opt) > 2):
-                    opt[1] = '='.join(opt[1:])
-                value = int(opt[1])
-            except ValueError:
-                if opt[1] == 'true':
-                    value = True
-                elif opt[1] == 'false':
-                    value = False
-                else:
-                    value = opt[1]
-            qmpcmd['arguments'][opt[0]] = value
-        return qmpcmd
-
-    def _execute_cmd(self, cmdline):
-        try:
-            qmpcmd = self.__build_cmd(cmdline)
-        except:
-            print 'command format: <command-name> ',
-            print '[arg-name1=arg1] ... [arg-nameN=argN]'
-            return True
-        resp = self.cmd_obj(qmpcmd)
-        if resp is None:
-            print 'Disconnected'
-            return False
-
-        if self._pp is not None:
-            self._pp.pprint(resp)
-        else:
-            print resp
-        return True
-
-    def connect(self):
-        self._greeting = qmp.QEMUMonitorProtocol.connect(self)
-        self.__completer_setup()
-
-    def show_banner(self, msg='Welcome to the QMP low-level shell!'):
-        print msg
-        version = self._greeting['QMP']['version']['qemu']
-        print 'Connected to QEMU %d.%d.%d\n' % (version['major'],version['minor'],version['micro'])
-
-    def read_exec_command(self, prompt):
-        """
-        Read and execute a command.
-
-        @return True if execution was ok, return False if disconnected.
-        """
-        try:
-            cmdline = raw_input(prompt)
-        except EOFError:
-            print
-            return False
-        if cmdline == '':
-            for ev in self.get_events():
-                print ev
-            self.clear_events()
-            return True
-        else:
-            return self._execute_cmd(cmdline)
-
-class HMPShell(QMPShell):
-    def __init__(self, address):
-        QMPShell.__init__(self, address)
-        self.__cpu_index = 0
-
-    def __cmd_completion(self):
-        for cmd in self.__cmd_passthrough('help')['return'].split('\r\n'):
-            if cmd and cmd[0] != '[' and cmd[0] != '\t':
-                name = cmd.split()[0] # drop help text
-                if name == 'info':
-                    continue
-                if name.find('|') != -1:
-                    # Command in the form 'foobar|f' or 'f|foobar', take the
-                    # full name
-                    opt = name.split('|')
-                    if len(opt[0]) == 1:
-                        name = opt[1]
-                    else:
-                        name = opt[0]
-                self._completer.append(name)
-                self._completer.append('help ' + name) # help completion
-
-    def __info_completion(self):
-        for cmd in self.__cmd_passthrough('info')['return'].split('\r\n'):
-            if cmd:
-                self._completer.append('info ' + cmd.split()[1])
-
-    def __other_completion(self):
-        # special cases
-        self._completer.append('help info')
-
-    def _fill_completion(self):
-        self.__cmd_completion()
-        self.__info_completion()
-        self.__other_completion()
-
-    def __cmd_passthrough(self, cmdline, cpu_index = 0):
-        return self.cmd_obj({ 'execute': 'human-monitor-command', 'arguments':
-                              { 'command-line': cmdline,
-                                'cpu-index': cpu_index } })
-
-    def _execute_cmd(self, cmdline):
-        if cmdline.split()[0] == "cpu":
-            # trap the cpu command, it requires special setting
-            try:
-                idx = int(cmdline.split()[1])
-                if not 'return' in self.__cmd_passthrough('info version', idx):
-                    print 'bad CPU index'
-                    return True
-                self.__cpu_index = idx
-            except ValueError:
-                print 'cpu command takes an integer argument'
-                return True
-        resp = self.__cmd_passthrough(cmdline, self.__cpu_index)
-        if resp is None:
-            print 'Disconnected'
-            return False
-        assert 'return' in resp or 'error' in resp
-        if 'return' in resp:
-            # Success
-            if len(resp['return']) > 0:
-                print resp['return'],
-        else:
-            # Error
-            print '%s: %s' % (resp['error']['class'], resp['error']['desc'])
-        return True
-
-    def show_banner(self):
-        QMPShell.show_banner(self, msg='Welcome to the HMP shell!')
-
-def die(msg):
-    sys.stderr.write('ERROR: %s\n' % msg)
-    sys.exit(1)
-
-def fail_cmdline(option=None):
-    if option:
-        sys.stderr.write('ERROR: bad command-line option \'%s\'\n' % option)
-    sys.stderr.write('qemu-shell [ -p ] [ -H ] < UNIX socket path> | < TCP address:port >\n')
-    sys.exit(1)
-
-def main():
-    addr = ''
-    qemu = None
-    hmp = False
-    pp = None
-
-    try:
-        for arg in sys.argv[1:]:
-            if arg == "-H":
-                if qemu is not None:
-                    fail_cmdline(arg)
-                hmp = True
-            elif arg == "-p":
-                if pp is not None:
-                    fail_cmdline(arg)
-                pp = pprint.PrettyPrinter(indent=4)
-            else:
-                if qemu is not None:
-                    fail_cmdline(arg)
-                if hmp:
-                    qemu = HMPShell(arg)
-                else:
-                    qemu = QMPShell(arg, pp)
-                addr = arg
-
-        if qemu is None:
-            fail_cmdline()
-    except QMPShellBadPort:
-        die('bad port number in command-line')
-
-    try:
-        qemu.connect()
-    except qmp.QMPConnectError:
-        die('Didn\'t get QMP greeting message')
-    except qmp.QMPCapabilitiesError:
-        die('Could not negotiate capabilities')
-    except qemu.error:
-        die('Could not connect to %s' % addr)
-
-    qemu.show_banner()
-    while qemu.read_exec_command('(QEMU) '):
-        pass
-    qemu.close()
-
-if __name__ == '__main__':
-    main()
diff --git a/QMP/qmp-spec.txt b/QMP/qmp-spec.txt
deleted file mode 100644 (file)
index a277896..0000000
+++ /dev/null
@@ -1,282 +0,0 @@
-           QEMU Monitor Protocol Specification - Version 0.1
-
-1. Introduction
-===============
-
-This document specifies the QEMU Monitor Protocol (QMP), a JSON-based protocol
-which is available for applications to control QEMU at the machine-level.
-
-To enable QMP support, QEMU has to be run in "control mode". This is done by
-starting QEMU with the appropriate command-line options. Please, refer to the
-QEMU manual page for more information.
-
-2. Protocol Specification
-=========================
-
-This section details the protocol format. For the purpose of this document
-"Client" is any application which is communicating with QEMU in control mode,
-and "Server" is QEMU itself.
-
-JSON data structures, when mentioned in this document, are always in the
-following format:
-
-    json-DATA-STRUCTURE-NAME
-
-Where DATA-STRUCTURE-NAME is any valid JSON data structure, as defined by
-the JSON standard:
-
-http://www.ietf.org/rfc/rfc4627.txt
-
-For convenience, json-object members and json-array elements mentioned in
-this document will be in a certain order. However, in real protocol usage
-they can be in ANY order, thus no particular order should be assumed.
-
-2.1 General Definitions
------------------------
-
-2.1.1 All interactions transmitted by the Server are json-objects, always
-      terminating with CRLF
-
-2.1.2 All json-objects members are mandatory when not specified otherwise
-
-2.2 Server Greeting
--------------------
-
-Right when connected the Server will issue a greeting message, which signals
-that the connection has been successfully established and that the Server is
-ready for capabilities negotiation (for more information refer to section
-'4. Capabilities Negotiation').
-
-The format is:
-
-{ "QMP": { "version": json-object, "capabilities": json-array } }
-
- Where,
-
-- The "version" member contains the Server's version information (the format
-  is the same of the 'query-version' command)
-- The "capabilities" member specify the availability of features beyond the
-  baseline specification
-
-2.3 Issuing Commands
---------------------
-
-The format for command execution is:
-
-{ "execute": json-string, "arguments": json-object, "id": json-value }
-
- Where,
-
-- The "execute" member identifies the command to be executed by the Server
-- The "arguments" member is used to pass any arguments required for the
-  execution of the command, it is optional when no arguments are required
-- The "id" member is a transaction identification associated with the
-  command execution, it is optional and will be part of the response if
-  provided
-
-2.4 Commands Responses
-----------------------
-
-There are two possible responses which the Server will issue as the result
-of a command execution: success or error.
-
-2.4.1 success
--------------
-
-The success response is issued when the command execution has finished
-without errors.
-
-The format is:
-
-{ "return": json-object, "id": json-value }
-
- Where,
-
-- The "return" member contains the command returned data, which is defined
-  in a per-command basis or an empty json-object if the command does not
-  return data
-- The "id" member contains the transaction identification associated
-  with the command execution (if issued by the Client)
-
-2.4.2 error
------------
-
-The error response is issued when the command execution could not be
-completed because of an error condition.
-
-The format is:
-
-{ "error": { "class": json-string, "desc": json-string }, "id": json-value }
-
- Where,
-
-- The "class" member contains the error class name (eg. "GenericError")
-- The "desc" member is a human-readable error message. Clients should
-  not attempt to parse this message.
-- The "id" member contains the transaction identification associated with
-  the command execution (if issued by the Client)
-
-NOTE: Some errors can occur before the Server is able to read the "id" member,
-in these cases the "id" member will not be part of the error response, even
-if provided by the client.
-
-2.5 Asynchronous events
------------------------
-
-As a result of state changes, the Server may send messages unilaterally
-to the Client at any time. They are called 'asynchronous events'.
-
-The format is:
-
-{ "event": json-string, "data": json-object,
-  "timestamp": { "seconds": json-number, "microseconds": json-number } }
-
- Where,
-
-- The "event" member contains the event's name
-- The "data" member contains event specific data, which is defined in a
-  per-event basis, it is optional
-- The "timestamp" member contains the exact time of when the event occurred
-  in the Server. It is a fixed json-object with time in seconds and
-  microseconds
-
-For a listing of supported asynchronous events, please, refer to the
-qmp-events.txt file.
-
-3. QMP Examples
-===============
-
-This section provides some examples of real QMP usage, in all of them
-'C' stands for 'Client' and 'S' stands for 'Server'.
-
-3.1 Server greeting
--------------------
-
-S: {"QMP": {"version": {"qemu": "0.12.50", "package": ""}, "capabilities": []}}
-
-3.2 Simple 'stop' execution
----------------------------
-
-C: { "execute": "stop" }
-S: {"return": {}}
-
-3.3 KVM information
--------------------
-
-C: { "execute": "query-kvm", "id": "example" }
-S: {"return": {"enabled": true, "present": true}, "id": "example"}
-
-3.4 Parsing error
-------------------
-
-C: { "execute": }
-S: {"error": {"class": "GenericError", "desc": "Invalid JSON syntax" } }
-
-3.5 Powerdown event
--------------------
-
-S: {"timestamp": {"seconds": 1258551470, "microseconds": 802384}, "event":
-"POWERDOWN"}
-
-4. Capabilities Negotiation
-----------------------------
-
-When a Client successfully establishes a connection, the Server is in
-Capabilities Negotiation mode.
-
-In this mode only the 'qmp_capabilities' command is allowed to run, all
-other commands will return the CommandNotFound error. Asynchronous messages
-are not delivered either.
-
-Clients should use the 'qmp_capabilities' command to enable capabilities
-advertised in the Server's greeting (section '2.2 Server Greeting') they
-support.
-
-When the 'qmp_capabilities' command is issued, and if it does not return an
-error, the Server enters in Command mode where capabilities changes take
-effect, all commands (except 'qmp_capabilities') are allowed and asynchronous
-messages are delivered.
-
-5 Compatibility Considerations
-------------------------------
-
-All protocol changes or new features which modify the protocol format in an
-incompatible way are disabled by default and will be advertised by the
-capabilities array (section '2.2 Server Greeting'). Thus, Clients can check
-that array and enable the capabilities they support.
-
-The QMP Server performs a type check on the arguments to a command.  It
-generates an error if a value does not have the expected type for its
-key, or if it does not understand a key that the Client included.  The
-strictness of the Server catches wrong assumptions of Clients about
-the Server's schema.  Clients can assume that, when such validation
-errors occur, they will be reported before the command generated any
-side effect.
-
-However, Clients must not assume any particular:
-
-- Length of json-arrays
-- Size of json-objects; in particular, future versions of QEMU may add
-  new keys and Clients should be able to ignore them.
-- Order of json-object members or json-array elements
-- Amount of errors generated by a command, that is, new errors can be added
-  to any existing command in newer versions of the Server
-
-Of course, the Server does guarantee to send valid JSON.  But apart from
-this, a Client should be "conservative in what they send, and liberal in
-what they accept".
-
-6. Downstream extension of QMP
-------------------------------
-
-We recommend that downstream consumers of QEMU do *not* modify QMP.
-Management tools should be able to support both upstream and downstream
-versions of QMP without special logic, and downstream extensions are
-inherently at odds with that.
-
-However, we recognize that it is sometimes impossible for downstreams to
-avoid modifying QMP.  Both upstream and downstream need to take care to
-preserve long-term compatibility and interoperability.
-
-To help with that, QMP reserves JSON object member names beginning with
-'__' (double underscore) for downstream use ("downstream names").  This
-means upstream will never use any downstream names for its commands,
-arguments, errors, asynchronous events, and so forth.
-
-Any new names downstream wishes to add must begin with '__'.  To
-ensure compatibility with other downstreams, it is strongly
-recommended that you prefix your downstram names with '__RFQDN_' where
-RFQDN is a valid, reverse fully qualified domain name which you
-control.  For example, a qemu-kvm specific monitor command would be:
-
-    (qemu) __org.linux-kvm_enable_irqchip
-
-Downstream must not change the server greeting (section 2.2) other than
-to offer additional capabilities.  But see below for why even that is
-discouraged.
-
-Section '5 Compatibility Considerations' applies to downstream as well
-as to upstream, obviously.  It follows that downstream must behave
-exactly like upstream for any input not containing members with
-downstream names ("downstream members"), except it may add members
-with downstream names to its output.
-
-Thus, a client should not be able to distinguish downstream from
-upstream as long as it doesn't send input with downstream members, and
-properly ignores any downstream members in the output it receives.
-
-Advice on downstream modifications:
-
-1. Introducing new commands is okay.  If you want to extend an existing
-   command, consider introducing a new one with the new behaviour
-   instead.
-
-2. Introducing new asynchronous messages is okay.  If you want to extend
-   an existing message, consider adding a new one instead.
-
-3. Introducing new errors for use in new commands is okay.  Adding new
-   errors to existing commands counts as extension, so 1. applies.
-
-4. New capabilities are strongly discouraged.  Capabilities are for
-   evolving the basic protocol, and multiple diverging basic protocol
-   dialects are most undesirable.
diff --git a/QMP/qmp.py b/QMP/qmp.py
deleted file mode 100644 (file)
index c551df1..0000000
+++ /dev/null
@@ -1,190 +0,0 @@
-# QEMU Monitor Protocol Python class
-# 
-# Copyright (C) 2009, 2010 Red Hat Inc.
-#
-# Authors:
-#  Luiz Capitulino <lcapitulino@redhat.com>
-#
-# This work is licensed under the terms of the GNU GPL, version 2.  See
-# the COPYING file in the top-level directory.
-
-import json
-import errno
-import socket
-
-class QMPError(Exception):
-    pass
-
-class QMPConnectError(QMPError):
-    pass
-
-class QMPCapabilitiesError(QMPError):
-    pass
-
-class QEMUMonitorProtocol:
-    def __init__(self, address, server=False):
-        """
-        Create a QEMUMonitorProtocol class.
-
-        @param address: QEMU address, can be either a unix socket path (string)
-                        or a tuple in the form ( address, port ) for a TCP
-                        connection
-        @param server: server mode listens on the socket (bool)
-        @raise socket.error on socket connection errors
-        @note No connection is established, this is done by the connect() or
-              accept() methods
-        """
-        self.__events = []
-        self.__address = address
-        self.__sock = self.__get_sock()
-        if server:
-            self.__sock.bind(self.__address)
-            self.__sock.listen(1)
-
-    def __get_sock(self):
-        if isinstance(self.__address, tuple):
-            family = socket.AF_INET
-        else:
-            family = socket.AF_UNIX
-        return socket.socket(family, socket.SOCK_STREAM)
-
-    def __negotiate_capabilities(self):
-        greeting = self.__json_read()
-        if greeting is None or not greeting.has_key('QMP'):
-            raise QMPConnectError
-        # Greeting seems ok, negotiate capabilities
-        resp = self.cmd('qmp_capabilities')
-        if "return" in resp:
-            return greeting
-        raise QMPCapabilitiesError
-
-    def __json_read(self, only_event=False):
-        while True:
-            data = self.__sockfile.readline()
-            if not data:
-                return
-            resp = json.loads(data)
-            if 'event' in resp:
-                self.__events.append(resp)
-                if not only_event:
-                    continue
-            return resp
-
-    error = socket.error
-
-    def connect(self, negotiate=True):
-        """
-        Connect to the QMP Monitor and perform capabilities negotiation.
-
-        @return QMP greeting dict
-        @raise socket.error on socket connection errors
-        @raise QMPConnectError if the greeting is not received
-        @raise QMPCapabilitiesError if fails to negotiate capabilities
-        """
-        self.__sock.connect(self.__address)
-        self.__sockfile = self.__sock.makefile()
-        if negotiate:
-            return self.__negotiate_capabilities()
-
-    def accept(self):
-        """
-        Await connection from QMP Monitor and perform capabilities negotiation.
-
-        @return QMP greeting dict
-        @raise socket.error on socket connection errors
-        @raise QMPConnectError if the greeting is not received
-        @raise QMPCapabilitiesError if fails to negotiate capabilities
-        """
-        self.__sock, _ = self.__sock.accept()
-        self.__sockfile = self.__sock.makefile()
-        return self.__negotiate_capabilities()
-
-    def cmd_obj(self, qmp_cmd):
-        """
-        Send a QMP command to the QMP Monitor.
-
-        @param qmp_cmd: QMP command to be sent as a Python dict
-        @return QMP response as a Python dict or None if the connection has
-                been closed
-        """
-        try:
-            self.__sock.sendall(json.dumps(qmp_cmd))
-        except socket.error, err:
-            if err[0] == errno.EPIPE:
-                return
-            raise socket.error(err)
-        return self.__json_read()
-
-    def cmd(self, name, args=None, id=None):
-        """
-        Build a QMP command and send it to the QMP Monitor.
-
-        @param name: command name (string)
-        @param args: command arguments (dict)
-        @param id: command id (dict, list, string or int)
-        """
-        qmp_cmd = { 'execute': name }
-        if args:
-            qmp_cmd['arguments'] = args
-        if id:
-            qmp_cmd['id'] = id
-        return self.cmd_obj(qmp_cmd)
-
-    def command(self, cmd, **kwds):
-        ret = self.cmd(cmd, kwds)
-        if ret.has_key('error'):
-            raise Exception(ret['error']['desc'])
-        return ret['return']
-
-    def pull_event(self, wait=False):
-        """
-        Get and delete the first available QMP event.
-
-        @param wait: block until an event is available (bool)
-        """
-        self.__sock.setblocking(0)
-        try:
-            self.__json_read()
-        except socket.error, err:
-            if err[0] == errno.EAGAIN:
-                # No data available
-                pass
-        self.__sock.setblocking(1)
-        if not self.__events and wait:
-            self.__json_read(only_event=True)
-        event = self.__events[0]
-        del self.__events[0]
-        return event
-
-    def get_events(self, wait=False):
-        """
-        Get a list of available QMP events.
-
-        @param wait: block until an event is available (bool)
-        """
-        self.__sock.setblocking(0)
-        try:
-            self.__json_read()
-        except socket.error, err:
-            if err[0] == errno.EAGAIN:
-                # No data available
-                pass
-        self.__sock.setblocking(1)
-        if not self.__events and wait:
-            self.__json_read(only_event=True)
-        return self.__events
-
-    def clear_events(self):
-        """
-        Clear current list of pending events.
-        """
-        self.__events = []
-
-    def close(self):
-        self.__sock.close()
-        self.__sockfile.close()
-
-    timeout = socket.timeout
-
-    def settimeout(self, timeout):
-        self.__sock.settimeout(timeout)
diff --git a/QMP/qom-fuse b/QMP/qom-fuse
deleted file mode 100755 (executable)
index 5c6754a..0000000
+++ /dev/null
@@ -1,138 +0,0 @@
-#!/usr/bin/python
-##
-# QEMU Object Model test tools
-#
-# Copyright IBM, Corp. 2012
-#
-# Authors:
-#  Anthony Liguori   <aliguori@us.ibm.com>
-#
-# This work is licensed under the terms of the GNU GPL, version 2 or later.  See
-# the COPYING file in the top-level directory.
-##
-
-import fuse, stat
-from fuse import Fuse
-import os, posix
-from errno import *
-from qmp import QEMUMonitorProtocol
-
-fuse.fuse_python_api = (0, 2)
-
-class QOMFS(Fuse):
-    def __init__(self, qmp, *args, **kwds):
-        Fuse.__init__(self, *args, **kwds)
-        self.qmp = qmp
-        self.qmp.connect()
-        self.ino_map = {}
-        self.ino_count = 1
-
-    def get_ino(self, path):
-        if self.ino_map.has_key(path):
-            return self.ino_map[path]
-        self.ino_map[path] = self.ino_count
-        self.ino_count += 1
-        return self.ino_map[path]
-
-    def is_object(self, path):
-        try:
-            items = self.qmp.command('qom-list', path=path)
-            return True
-        except:
-            return False
-
-    def is_property(self, path):
-        try:
-            path, prop = path.rsplit('/', 1)
-            for item in self.qmp.command('qom-list', path=path):
-                if item['name'] == prop:
-                    return True
-            return False
-        except:
-            return False
-
-    def is_link(self, path):
-        try:
-            path, prop = path.rsplit('/', 1)
-            for item in self.qmp.command('qom-list', path=path):
-                if item['name'] == prop:
-                    if item['type'].startswith('link<'):
-                        return True
-                    return False
-            return False
-        except:
-            return False
-
-    def read(self, path, length, offset):
-        if not self.is_property(path):
-            return -ENOENT
-
-        path, prop = path.rsplit('/', 1)
-        try:
-            data = str(self.qmp.command('qom-get', path=path, property=prop))
-            data += '\n' # make values shell friendly
-        except:
-            return -EPERM
-
-        if offset > len(data):
-            return ''
-
-        return str(data[offset:][:length])
-
-    def readlink(self, path):
-        if not self.is_link(path):
-            return False
-        path, prop = path.rsplit('/', 1)
-        prefix = '/'.join(['..'] * (len(path.split('/')) - 1))
-        return prefix + str(self.qmp.command('qom-get', path=path,
-                                             property=prop))
-
-    def getattr(self, path):
-        if self.is_link(path):
-            value = posix.stat_result((0755 | stat.S_IFLNK,
-                                       self.get_ino(path),
-                                       0,
-                                       2,
-                                       1000,
-                                       1000,
-                                       4096,
-                                       0,
-                                       0,
-                                       0))
-        elif self.is_object(path):
-            value = posix.stat_result((0755 | stat.S_IFDIR,
-                                       self.get_ino(path),
-                                       0,
-                                       2,
-                                       1000,
-                                       1000,
-                                       4096,
-                                       0,
-                                       0,
-                                       0))
-        elif self.is_property(path):
-            value = posix.stat_result((0644 | stat.S_IFREG,
-                                       self.get_ino(path),
-                                       0,
-                                       1,
-                                       1000,
-                                       1000,
-                                       4096,
-                                       0,
-                                       0,
-                                       0))
-        else:
-            value = -ENOENT
-        return value
-
-    def readdir(self, path, offset):
-        yield fuse.Direntry('.')
-        yield fuse.Direntry('..')
-        for item in self.qmp.command('qom-list', path=path):
-            yield fuse.Direntry(str(item['name']))
-
-if __name__ == '__main__':
-    import sys, os
-
-    fs = QOMFS(QEMUMonitorProtocol(os.environ['QMP_SOCKET']))
-    fs.main(sys.argv)
diff --git a/QMP/qom-get b/QMP/qom-get
deleted file mode 100755 (executable)
index 0172c69..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-#!/usr/bin/python
-##
-# QEMU Object Model test tools
-#
-# Copyright IBM, Corp. 2011
-#
-# Authors:
-#  Anthony Liguori   <aliguori@us.ibm.com>
-#
-# This work is licensed under the terms of the GNU GPL, version 2 or later.  See
-# the COPYING file in the top-level directory.
-##
-
-import sys
-import os
-from qmp import QEMUMonitorProtocol
-
-cmd, args = sys.argv[0], sys.argv[1:]
-socket_path = None
-path = None
-prop = None
-
-def usage():
-    return '''environment variables:
-    QMP_SOCKET=<path | addr:port>
-usage:
-    %s [-h] [-s <QMP socket path | addr:port>] <path>.<property>
-''' % cmd
-
-def usage_error(error_msg = "unspecified error"):
-    sys.stderr.write('%s\nERROR: %s\n' % (usage(), error_msg))
-    exit(1)
-
-if len(args) > 0:
-    if args[0] == "-h":
-        print usage()
-        exit(0);
-    elif args[0] == "-s":
-        try:
-            socket_path = args[1]
-        except:
-            usage_error("missing argument: QMP socket path or address");
-        args = args[2:]
-
-if not socket_path:
-    if os.environ.has_key('QMP_SOCKET'):
-        socket_path = os.environ['QMP_SOCKET']
-    else:
-        usage_error("no QMP socket path or address given");
-
-if len(args) > 0:
-    try:
-        path, prop = args[0].rsplit('.', 1)
-    except:
-        usage_error("invalid format for path/property/value")
-else:
-    usage_error("not enough arguments")
-
-srv = QEMUMonitorProtocol(socket_path)
-srv.connect()
-
-rsp = srv.command('qom-get', path=path, property=prop)
-if type(rsp) == dict:
-    for i in rsp.keys():
-        print '%s: %s' % (i, rsp[i])
-else:
-    print rsp
diff --git a/QMP/qom-list b/QMP/qom-list
deleted file mode 100755 (executable)
index 1e7cc6c..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-#!/usr/bin/python
-##
-# QEMU Object Model test tools
-#
-# Copyright IBM, Corp. 2011
-#
-# Authors:
-#  Anthony Liguori   <aliguori@us.ibm.com>
-#
-# This work is licensed under the terms of the GNU GPL, version 2 or later.  See
-# the COPYING file in the top-level directory.
-##
-
-import sys
-import os
-from qmp import QEMUMonitorProtocol
-
-cmd, args = sys.argv[0], sys.argv[1:]
-socket_path = None
-path = None
-prop = None
-
-def usage():
-    return '''environment variables:
-    QMP_SOCKET=<path | addr:port>
-usage:
-    %s [-h] [-s <QMP socket path | addr:port>] [<path>]
-''' % cmd
-
-def usage_error(error_msg = "unspecified error"):
-    sys.stderr.write('%s\nERROR: %s\n' % (usage(), error_msg))
-    exit(1)
-
-if len(args) > 0:
-    if args[0] == "-h":
-        print usage()
-        exit(0);
-    elif args[0] == "-s":
-        try:
-            socket_path = args[1]
-        except:
-            usage_error("missing argument: QMP socket path or address");
-        args = args[2:]
-
-if not socket_path:
-    if os.environ.has_key('QMP_SOCKET'):
-        socket_path = os.environ['QMP_SOCKET']
-    else:
-        usage_error("no QMP socket path or address given");
-
-srv = QEMUMonitorProtocol(socket_path)
-srv.connect()
-
-if len(args) == 0:
-    print '/'
-    sys.exit(0)
-
-for item in srv.command('qom-list', path=args[0]):
-    if item['type'].startswith('child<'):
-        print '%s/' % item['name']
-    elif item['type'].startswith('link<'):
-        print '@%s/' % item['name']
-    else:
-        print '%s' % item['name']
diff --git a/QMP/qom-set b/QMP/qom-set
deleted file mode 100755 (executable)
index 54ecfec..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-#!/usr/bin/python
-##
-# QEMU Object Model test tools
-#
-# Copyright IBM, Corp. 2011
-#
-# Authors:
-#  Anthony Liguori   <aliguori@us.ibm.com>
-#
-# This work is licensed under the terms of the GNU GPL, version 2 or later.  See
-# the COPYING file in the top-level directory.
-##
-
-import sys
-import os
-from qmp import QEMUMonitorProtocol
-
-cmd, args = sys.argv[0], sys.argv[1:]
-socket_path = None
-path = None
-prop = None
-value = None
-
-def usage():
-    return '''environment variables:
-    QMP_SOCKET=<path | addr:port>
-usage:
-    %s [-h] [-s <QMP socket path | addr:port>] <path>.<property> <value>
-''' % cmd
-
-def usage_error(error_msg = "unspecified error"):
-    sys.stderr.write('%s\nERROR: %s\n' % (usage(), error_msg))
-    exit(1)
-
-if len(args) > 0:
-    if args[0] == "-h":
-        print usage()
-        exit(0);
-    elif args[0] == "-s":
-        try:
-            socket_path = args[1]
-        except:
-            usage_error("missing argument: QMP socket path or address");
-        args = args[2:]
-
-if not socket_path:
-    if os.environ.has_key('QMP_SOCKET'):
-        socket_path = os.environ['QMP_SOCKET']
-    else:
-        usage_error("no QMP socket path or address given");
-
-if len(args) > 1:
-    try:
-        path, prop = args[0].rsplit('.', 1)
-    except:
-        usage_error("invalid format for path/property/value")
-    value = args[1]
-else:
-    usage_error("not enough arguments")
-
-srv = QEMUMonitorProtocol(socket_path)
-srv.connect()
-
-print srv.command('qom-set', path=path, property=prop, value=sys.argv[2])
diff --git a/block.c b/block.c
index a325efcb215a51d52f02408beb57209b6aa11949..e176c6f3bcba6169ca57c0aba0ee055b41f1a8f2 100644 (file)
--- a/block.c
+++ b/block.c
@@ -394,18 +394,26 @@ typedef struct CreateCo {
     char *filename;
     QEMUOptionParameter *options;
     int ret;
+    Error *err;
 } CreateCo;
 
 static void coroutine_fn bdrv_create_co_entry(void *opaque)
 {
+    Error *local_err = NULL;
+    int ret;
+
     CreateCo *cco = opaque;
     assert(cco->drv);
 
-    cco->ret = cco->drv->bdrv_create(cco->filename, cco->options);
+    ret = cco->drv->bdrv_create(cco->filename, cco->options, &local_err);
+    if (error_is_set(&local_err)) {
+        error_propagate(&cco->err, local_err);
+    }
+    cco->ret = ret;
 }
 
 int bdrv_create(BlockDriver *drv, const char* filename,
-    QEMUOptionParameter *options)
+    QEMUOptionParameter *options, Error **errp)
 {
     int ret;
 
@@ -415,9 +423,11 @@ int bdrv_create(BlockDriver *drv, const char* filename,
         .filename = g_strdup(filename),
         .options = options,
         .ret = NOT_DONE,
+        .err = NULL,
     };
 
     if (!drv->bdrv_create) {
+        error_setg(errp, "Driver '%s' does not support image creation", drv->format_name);
         ret = -ENOTSUP;
         goto out;
     }
@@ -434,22 +444,37 @@ int bdrv_create(BlockDriver *drv, const char* filename,
     }
 
     ret = cco.ret;
+    if (ret < 0) {
+        if (error_is_set(&cco.err)) {
+            error_propagate(errp, cco.err);
+        } else {
+            error_setg_errno(errp, -ret, "Could not create image");
+        }
+    }
 
 out:
     g_free(cco.filename);
     return ret;
 }
 
-int bdrv_create_file(const char* filename, QEMUOptionParameter *options)
+int bdrv_create_file(const char* filename, QEMUOptionParameter *options,
+                     Error **errp)
 {
     BlockDriver *drv;
+    Error *local_err = NULL;
+    int ret;
 
     drv = bdrv_find_protocol(filename, true);
     if (drv == NULL) {
+        error_setg(errp, "Could not find protocol for file '%s'", filename);
         return -ENOENT;
     }
 
-    return bdrv_create(drv, filename, options);
+    ret = bdrv_create(drv, filename, options, &local_err);
+    if (error_is_set(&local_err)) {
+        error_propagate(errp, local_err);
+    }
+    return ret;
 }
 
 /*
@@ -552,7 +577,7 @@ BlockDriver *bdrv_find_protocol(const char *filename,
 }
 
 static int find_image_format(BlockDriverState *bs, const char *filename,
-                             BlockDriver **pdrv)
+                             BlockDriver **pdrv, Error **errp)
 {
     int score, score_max;
     BlockDriver *drv1, *drv;
@@ -563,6 +588,7 @@ static int find_image_format(BlockDriverState *bs, const char *filename,
     if (bs->sg || !bdrv_is_inserted(bs) || bdrv_getlength(bs) == 0) {
         drv = bdrv_find_format("raw");
         if (!drv) {
+            error_setg(errp, "Could not find raw image format");
             ret = -ENOENT;
         }
         *pdrv = drv;
@@ -571,6 +597,8 @@ static int find_image_format(BlockDriverState *bs, const char *filename,
 
     ret = bdrv_pread(bs, 0, buf, sizeof(buf));
     if (ret < 0) {
+        error_setg_errno(errp, -ret, "Could not read image for determining its "
+                         "format");
         *pdrv = NULL;
         return ret;
     }
@@ -587,6 +615,8 @@ static int find_image_format(BlockDriverState *bs, const char *filename,
         }
     }
     if (!drv) {
+        error_setg(errp, "Could not determine image format: No compatible "
+                   "driver found");
         ret = -ENOENT;
     }
     *pdrv = drv;
@@ -706,10 +736,11 @@ static int bdrv_open_flags(BlockDriverState *bs, int flags)
  * Removes all processed options from *options.
  */
 static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
-    QDict *options, int flags, BlockDriver *drv)
+    QDict *options, int flags, BlockDriver *drv, Error **errp)
 {
     int ret, open_flags;
     const char *filename;
+    Error *local_err = NULL;
 
     assert(drv != NULL);
     assert(bs->file == NULL);
@@ -738,6 +769,7 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
     bs->read_only = !(open_flags & BDRV_O_RDWR);
 
     if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, bs->read_only)) {
+        error_setg(errp, "Driver '%s' is not whitelisted", drv->format_name);
         return -ENOTSUP;
     }
 
@@ -761,25 +793,32 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
     if (drv->bdrv_file_open) {
         assert(file == NULL);
         assert(drv->bdrv_parse_filename || filename != NULL);
-        ret = drv->bdrv_file_open(bs, options, open_flags);
+        ret = drv->bdrv_file_open(bs, options, open_flags, &local_err);
     } else {
         if (file == NULL) {
-            qerror_report(ERROR_CLASS_GENERIC_ERROR, "Can't use '%s' as a "
-                          "block driver for the protocol level",
-                          drv->format_name);
+            error_setg(errp, "Can't use '%s' as a block driver for the "
+                       "protocol level", drv->format_name);
             ret = -EINVAL;
             goto free_and_fail;
         }
         bs->file = file;
-        ret = drv->bdrv_open(bs, options, open_flags);
+        ret = drv->bdrv_open(bs, options, open_flags, &local_err);
     }
 
     if (ret < 0) {
+        if (error_is_set(&local_err)) {
+            error_propagate(errp, local_err);
+        } else if (filename) {
+            error_setg_errno(errp, -ret, "Could not open '%s'", filename);
+        } else {
+            error_setg_errno(errp, -ret, "Could not open image");
+        }
         goto free_and_fail;
     }
 
     ret = refresh_total_sectors(bs, bs->total_sectors);
     if (ret < 0) {
+        error_setg_errno(errp, -ret, "Could not refresh total sector count");
         goto free_and_fail;
     }
 
@@ -808,12 +847,13 @@ free_and_fail:
  * dictionary, it needs to use QINCREF() before calling bdrv_file_open.
  */
 int bdrv_file_open(BlockDriverState **pbs, const char *filename,
-                   QDict *options, int flags)
+                   QDict *options, int flags, Error **errp)
 {
     BlockDriverState *bs;
     BlockDriver *drv;
     const char *drvname;
     bool allow_protocol_prefix = false;
+    Error *local_err = NULL;
     int ret;
 
     /* NULL means an empty set of options */
@@ -832,8 +872,8 @@ int bdrv_file_open(BlockDriverState **pbs, const char *filename,
         qdict_put(options, "filename", qstring_from_str(filename));
         allow_protocol_prefix = true;
     } else {
-        qerror_report(ERROR_CLASS_GENERIC_ERROR, "Can't specify 'file' and "
-                      "'filename' options at the same time");
+        error_setg(errp, "Can't specify 'file' and 'filename' options at the "
+                   "same time");
         ret = -EINVAL;
         goto fail;
     }
@@ -842,53 +882,53 @@ int bdrv_file_open(BlockDriverState **pbs, const char *filename,
     drvname = qdict_get_try_str(options, "driver");
     if (drvname) {
         drv = bdrv_find_whitelisted_format(drvname, !(flags & BDRV_O_RDWR));
+        if (!drv) {
+            error_setg(errp, "Unknown driver '%s'", drvname);
+        }
         qdict_del(options, "driver");
     } else if (filename) {
         drv = bdrv_find_protocol(filename, allow_protocol_prefix);
         if (!drv) {
-            qerror_report(ERROR_CLASS_GENERIC_ERROR, "Unknown protocol");
+            error_setg(errp, "Unknown protocol");
         }
     } else {
-        qerror_report(ERROR_CLASS_GENERIC_ERROR,
-                      "Must specify either driver or file");
+        error_setg(errp, "Must specify either driver or file");
         drv = NULL;
     }
 
     if (!drv) {
+        /* errp has been set already */
         ret = -ENOENT;
         goto fail;
     }
 
     /* Parse the filename and open it */
     if (drv->bdrv_parse_filename && filename) {
-        Error *local_err = NULL;
         drv->bdrv_parse_filename(filename, options, &local_err);
         if (error_is_set(&local_err)) {
-            qerror_report_err(local_err);
-            error_free(local_err);
+            error_propagate(errp, local_err);
             ret = -EINVAL;
             goto fail;
         }
         qdict_del(options, "filename");
     } else if (!drv->bdrv_parse_filename && !filename) {
-        qerror_report(ERROR_CLASS_GENERIC_ERROR,
-                      "The '%s' block driver requires a file name",
-                      drv->format_name);
+        error_setg(errp, "The '%s' block driver requires a file name",
+                   drv->format_name);
         ret = -EINVAL;
         goto fail;
     }
 
-    ret = bdrv_open_common(bs, NULL, options, flags, drv);
+    ret = bdrv_open_common(bs, NULL, options, flags, drv, &local_err);
     if (ret < 0) {
+        error_propagate(errp, local_err);
         goto fail;
     }
 
     /* Check if any unknown options were used */
     if (qdict_size(options) != 0) {
         const QDictEntry *entry = qdict_first(options);
-        qerror_report(ERROR_CLASS_GENERIC_ERROR, "Block protocol '%s' doesn't "
-                      "support the option '%s'",
-                      drv->format_name, entry->key);
+        error_setg(errp, "Block protocol '%s' doesn't support the option '%s'",
+                   drv->format_name, entry->key);
         ret = -EINVAL;
         goto fail;
     }
@@ -915,11 +955,12 @@ fail:
  * function (even on failure), so if the caller intends to reuse the dictionary,
  * it needs to use QINCREF() before calling bdrv_file_open.
  */
-int bdrv_open_backing_file(BlockDriverState *bs, QDict *options)
+int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp)
 {
     char backing_filename[PATH_MAX];
     int back_flags, ret;
     BlockDriver *back_drv = NULL;
+    Error *local_err = NULL;
 
     if (bs->backing_hd != NULL) {
         QDECREF(options);
@@ -952,11 +993,12 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options)
 
     ret = bdrv_open(bs->backing_hd,
                     *backing_filename ? backing_filename : NULL, options,
-                    back_flags, back_drv);
+                    back_flags, back_drv, &local_err);
     if (ret < 0) {
         bdrv_unref(bs->backing_hd);
         bs->backing_hd = NULL;
         bs->open_flags |= BDRV_O_NO_BACKING;
+        error_propagate(errp, local_err);
         return ret;
     }
     return 0;
@@ -990,7 +1032,7 @@ static void extract_subqdict(QDict *src, QDict **dst, const char *start)
  * dictionary, it needs to use QINCREF() before calling bdrv_open.
  */
 int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
-              int flags, BlockDriver *drv)
+              int flags, BlockDriver *drv, Error **errp)
 {
     int ret;
     /* TODO: extra byte is a hack to ensure MAX_PATH space on Windows. */
@@ -998,6 +1040,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
     BlockDriverState *file = NULL;
     QDict *file_options = NULL;
     const char *drvname;
+    Error *local_err = NULL;
 
     /* NULL means an empty set of options */
     if (options == NULL) {
@@ -1016,7 +1059,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
         char backing_filename[PATH_MAX];
 
         if (qdict_size(options) != 0) {
-            error_report("Can't use snapshot=on with driver-specific options");
+            error_setg(errp, "Can't use snapshot=on with driver-specific options");
             ret = -EINVAL;
             goto fail;
         }
@@ -1027,7 +1070,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
 
         /* if there is a backing file, use it */
         bs1 = bdrv_new("");
-        ret = bdrv_open(bs1, filename, NULL, 0, drv);
+        ret = bdrv_open(bs1, filename, NULL, 0, drv, &local_err);
         if (ret < 0) {
             bdrv_unref(bs1);
             goto fail;
@@ -1038,6 +1081,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
 
         ret = get_tmp_filename(tmp_filename, sizeof(tmp_filename));
         if (ret < 0) {
+            error_setg_errno(errp, -ret, "Could not get temporary filename");
             goto fail;
         }
 
@@ -1046,6 +1090,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
             snprintf(backing_filename, sizeof(backing_filename),
                      "%s", filename);
         } else if (!realpath(filename, backing_filename)) {
+            error_setg_errno(errp, errno, "Could not resolve path '%s'", filename);
             ret = -errno;
             goto fail;
         }
@@ -1062,9 +1107,14 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
                 drv->format_name);
         }
 
-        ret = bdrv_create(bdrv_qcow2, tmp_filename, create_options);
+        ret = bdrv_create(bdrv_qcow2, tmp_filename, create_options, &local_err);
         free_option_parameters(create_options);
         if (ret < 0) {
+            error_setg_errno(errp, -ret, "Could not create temporary overlay "
+                             "'%s': %s", tmp_filename,
+                             error_get_pretty(local_err));
+            error_free(local_err);
+            local_err = NULL;
             goto fail;
         }
 
@@ -1081,7 +1131,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
     extract_subqdict(options, &file_options, "file.");
 
     ret = bdrv_file_open(&file, filename, file_options,
-                         bdrv_open_flags(bs, flags | BDRV_O_UNMAP));
+                         bdrv_open_flags(bs, flags | BDRV_O_UNMAP), &local_err);
     if (ret < 0) {
         goto fail;
     }
@@ -1094,7 +1144,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
     }
 
     if (!drv) {
-        ret = find_image_format(file, filename, &drv);
+        ret = find_image_format(file, filename, &drv, &local_err);
     }
 
     if (!drv) {
@@ -1102,7 +1152,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
     }
 
     /* Open the image */
-    ret = bdrv_open_common(bs, file, options, flags, drv);
+    ret = bdrv_open_common(bs, file, options, flags, drv, &local_err);
     if (ret < 0) {
         goto unlink_and_fail;
     }
@@ -1117,7 +1167,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
         QDict *backing_options;
 
         extract_subqdict(options, &backing_options, "backing.");
-        ret = bdrv_open_backing_file(bs, backing_options);
+        ret = bdrv_open_backing_file(bs, backing_options, &local_err);
         if (ret < 0) {
             goto close_and_fail;
         }
@@ -1126,9 +1176,9 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
     /* Check if any unknown options were used */
     if (qdict_size(options) != 0) {
         const QDictEntry *entry = qdict_first(options);
-        qerror_report(ERROR_CLASS_GENERIC_ERROR, "Block format '%s' used by "
-            "device '%s' doesn't support the option '%s'",
-            drv->format_name, bs->device_name, entry->key);
+        error_setg(errp, "Block format '%s' used by device '%s' doesn't "
+                   "support the option '%s'", drv->format_name, bs->device_name,
+                   entry->key);
 
         ret = -EINVAL;
         goto close_and_fail;
@@ -1152,11 +1202,17 @@ fail:
     QDECREF(bs->options);
     QDECREF(options);
     bs->options = NULL;
+    if (error_is_set(&local_err)) {
+        error_propagate(errp, local_err);
+    }
     return ret;
 
 close_and_fail:
     bdrv_close(bs);
     QDECREF(options);
+    if (error_is_set(&local_err)) {
+        error_propagate(errp, local_err);
+    }
     return ret;
 }
 
@@ -4433,6 +4489,7 @@ void bdrv_img_create(const char *filename, const char *fmt,
     BlockDriverState *bs = NULL;
     BlockDriver *drv, *proto_drv;
     BlockDriver *backing_drv = NULL;
+    Error *local_err = NULL;
     int ret = 0;
 
     /* Find driver and parse its options */
@@ -4519,10 +4576,13 @@ void bdrv_img_create(const char *filename, const char *fmt,
             bs = bdrv_new("");
 
             ret = bdrv_open(bs, backing_file->value.s, NULL, back_flags,
-                            backing_drv);
+                            backing_drv, &local_err);
             if (ret < 0) {
-                error_setg_errno(errp, -ret, "Could not open '%s'",
-                                 backing_file->value.s);
+                error_setg_errno(errp, -ret, "Could not open '%s': %s",
+                                 backing_file->value.s,
+                                 error_get_pretty(local_err));
+                error_free(local_err);
+                local_err = NULL;
                 goto out;
             }
             bdrv_get_geometry(bs, &size);
@@ -4541,22 +4601,19 @@ void bdrv_img_create(const char *filename, const char *fmt,
         print_option_parameters(param);
         puts("");
     }
-    ret = bdrv_create(drv, filename, param);
-    if (ret < 0) {
-        if (ret == -ENOTSUP) {
-            error_setg(errp,"Formatting or formatting option not supported for "
-                            "file format '%s'", fmt);
-        } else if (ret == -EFBIG) {
-            const char *cluster_size_hint = "";
-            if (get_option_parameter(create_options, BLOCK_OPT_CLUSTER_SIZE)) {
-                cluster_size_hint = " (try using a larger cluster size)";
-            }
-            error_setg(errp, "The image size is too large for file format '%s'%s",
-                       fmt, cluster_size_hint);
-        } else {
-            error_setg(errp, "%s: error while creating %s: %s", filename, fmt,
-                       strerror(-ret));
+    ret = bdrv_create(drv, filename, param, &local_err);
+    if (ret == -EFBIG) {
+        /* This is generally a better message than whatever the driver would
+         * deliver (especially because of the cluster_size_hint), since that
+         * is most probably not much different from "image too large". */
+        const char *cluster_size_hint = "";
+        if (get_option_parameter(create_options, BLOCK_OPT_CLUSTER_SIZE)) {
+            cluster_size_hint = " (try using a larger cluster size)";
         }
+        error_setg(errp, "The image size is too large for file format '%s'"
+                   "%s", fmt, cluster_size_hint);
+        error_free(local_err);
+        local_err = NULL;
     }
 
 out:
@@ -4566,6 +4623,9 @@ out:
     if (bs) {
         bdrv_unref(bs);
     }
+    if (error_is_set(&local_err)) {
+        error_propagate(errp, local_err);
+    }
 }
 
 AioContext *bdrv_get_aio_context(BlockDriverState *bs)
@@ -4579,3 +4639,11 @@ void bdrv_add_before_write_notifier(BlockDriverState *bs,
 {
     notifier_with_return_list_add(&bs->before_write_notifiers, notifier);
 }
+
+int bdrv_amend_options(BlockDriverState *bs, QEMUOptionParameter *options)
+{
+    if (bs->drv->bdrv_amend_options == NULL) {
+        return -ENOTSUP;
+    }
+    return bs->drv->bdrv_amend_options(bs, options);
+}
index 5d33e036086eab82aa6d2c39c73b33b478e7c251..be948b2fdd8cc261c1013110e8c646e3ee9d5562 100644 (file)
@@ -350,7 +350,8 @@ static QemuOptsList runtime_opts = {
     },
 };
 
-static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags)
+static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
+                         Error **errp)
 {
     BDRVBlkdebugState *s = bs->opaque;
     QemuOpts *opts;
@@ -386,8 +387,10 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags)
         goto fail;
     }
 
-    ret = bdrv_file_open(&bs->file, filename, NULL, flags);
+    ret = bdrv_file_open(&bs->file, filename, NULL, flags, &local_err);
     if (ret < 0) {
+        qerror_report_err(local_err);
+        error_free(local_err);
         goto fail;
     }
 
index c4e961eeb11817962f27d2708da5c654f6f81bdb..bff95d2a450057c7bae021ff564d08ca93e97f3c 100644 (file)
@@ -116,7 +116,8 @@ static QemuOptsList runtime_opts = {
     },
 };
 
-static int blkverify_open(BlockDriverState *bs, QDict *options, int flags)
+static int blkverify_open(BlockDriverState *bs, QDict *options, int flags,
+                          Error **errp)
 {
     BDRVBlkverifyState *s = bs->opaque;
     QemuOpts *opts;
@@ -140,8 +141,10 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags)
         goto fail;
     }
 
-    ret = bdrv_file_open(&bs->file, raw, NULL, flags);
+    ret = bdrv_file_open(&bs->file, raw, NULL, flags, &local_err);
     if (ret < 0) {
+        qerror_report_err(local_err);
+        error_free(local_err);
         goto fail;
     }
 
@@ -153,8 +156,10 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags)
     }
 
     s->test_file = bdrv_new("");
-    ret = bdrv_open(s->test_file, filename, NULL, flags, NULL);
+    ret = bdrv_open(s->test_file, filename, NULL, flags, NULL, &local_err);
     if (ret < 0) {
+        qerror_report_err(local_err);
+        error_free(local_err);
         bdrv_unref(s->test_file);
         s->test_file = NULL;
         goto fail;
index d7078c0775c184e638202af4c79f76b34fb44fb0..51d9a905772e1f989abc018910178d90bb8a4b70 100644 (file)
@@ -108,7 +108,8 @@ static int bochs_probe(const uint8_t *buf, int buf_size, const char *filename)
     return 0;
 }
 
-static int bochs_open(BlockDriverState *bs, QDict *options, int flags)
+static int bochs_open(BlockDriverState *bs, QDict *options, int flags,
+                      Error **errp)
 {
     BDRVBochsState *s = bs->opaque;
     int i;
index 6ea7cf404647fe16986b148c3b7ba4bf1a469340..b907023e106a9de628d7fc8725810c2335849030 100644 (file)
@@ -53,7 +53,8 @@ static int cloop_probe(const uint8_t *buf, int buf_size, const char *filename)
     return 0;
 }
 
-static int cloop_open(BlockDriverState *bs, QDict *options, int flags)
+static int cloop_open(BlockDriverState *bs, QDict *options, int flags,
+                      Error **errp)
 {
     BDRVCloopState *s = bs->opaque;
     uint32_t offsets_size, max_compressed_block_size = 1, i;
index 764b93fae06a9ea0b188ec62639d5efa2172b7a4..909c3e718293b1f10a568057bb86977b430b9381 100644 (file)
@@ -58,7 +58,8 @@ static int cow_probe(const uint8_t *buf, int buf_size, const char *filename)
         return 0;
 }
 
-static int cow_open(BlockDriverState *bs, QDict *options, int flags)
+static int cow_open(BlockDriverState *bs, QDict *options, int flags,
+                    Error **errp)
 {
     BDRVCowState *s = bs->opaque;
     struct cow_header_v2 cow_header;
@@ -294,12 +295,14 @@ static void cow_close(BlockDriverState *bs)
 {
 }
 
-static int cow_create(const char *filename, QEMUOptionParameter *options)
+static int cow_create(const char *filename, QEMUOptionParameter *options,
+                      Error **errp)
 {
     struct cow_header_v2 cow_header;
     struct stat st;
     int64_t image_sectors = 0;
     const char *image_filename = NULL;
+    Error *local_err = NULL;
     int ret;
     BlockDriverState *cow_bs;
 
@@ -313,13 +316,17 @@ static int cow_create(const char *filename, QEMUOptionParameter *options)
         options++;
     }
 
-    ret = bdrv_create_file(filename, options);
+    ret = bdrv_create_file(filename, options, &local_err);
     if (ret < 0) {
+        qerror_report_err(local_err);
+        error_free(local_err);
         return ret;
     }
 
-    ret = bdrv_file_open(&cow_bs, filename, NULL, BDRV_O_RDWR);
+    ret = bdrv_file_open(&cow_bs, filename, NULL, BDRV_O_RDWR, &local_err);
     if (ret < 0) {
+        qerror_report_err(local_err);
+        error_free(local_err);
         return ret;
     }
 
index ca2cedcec16c2ec411705b19a5394b9b8577485e..5a46f9707cef9475959cf377aa77d3f9f16b9db5 100644 (file)
@@ -395,7 +395,8 @@ static QemuOptsList runtime_opts = {
     },
 };
 
-static int curl_open(BlockDriverState *bs, QDict *options, int flags)
+static int curl_open(BlockDriverState *bs, QDict *options, int flags,
+                     Error **errp)
 {
     BDRVCURLState *s = bs->opaque;
     CURLState *state = NULL;
index 3141cb5b889a4972eb40a3e09e761cac85db2bfa..d5e9b1ff0163fb80b4830f5c724b11335ce29149 100644 (file)
@@ -92,7 +92,8 @@ static int read_uint32(BlockDriverState *bs, int64_t offset, uint32_t *result)
     return 0;
 }
 
-static int dmg_open(BlockDriverState *bs, QDict *options, int flags)
+static int dmg_open(BlockDriverState *bs, QDict *options, int flags,
+                    Error **errp)
 {
     BDRVDMGState *s = bs->opaque;
     uint64_t info_begin,info_end,last_in_offset,last_out_offset;
index dbb03f4de50fdee18e8f3dc48a99b9ce41279322..256de10ed3975356a631307e8d5b263ef144e6bf 100644 (file)
@@ -288,7 +288,7 @@ static QemuOptsList runtime_opts = {
 };
 
 static int qemu_gluster_open(BlockDriverState *bs,  QDict *options,
-                             int bdrv_flags)
+                             int bdrv_flags, Error **errp)
 {
     BDRVGlusterState *s = bs->opaque;
     int open_flags = O_BINARY;
@@ -357,7 +357,7 @@ out:
 }
 
 static int qemu_gluster_create(const char *filename,
-        QEMUOptionParameter *options)
+        QEMUOptionParameter *options, Error **errp)
 {
     struct glfs *glfs;
     struct glfs_fd *fd;
index 813abd8fef7ef0069870abcbb8ca8c329258b049..44603828117b660794dd667a1445c0dd53b78747 100644 (file)
@@ -33,6 +33,8 @@
 #include "trace.h"
 #include "block/scsi.h"
 #include "qemu/iov.h"
+#include "sysemu/sysemu.h"
+#include "qmp-commands.h"
 
 #include <iscsi/iscsi.h>
 #include <iscsi/scsi-lowlevel.h>
@@ -50,8 +52,21 @@ typedef struct IscsiLun {
     uint64_t num_blocks;
     int events;
     QEMUTimer *nop_timer;
+    uint8_t lbpme;
+    uint8_t lbprz;
+    struct scsi_inquiry_logical_block_provisioning lbp;
+    struct scsi_inquiry_block_limits bl;
 } IscsiLun;
 
+typedef struct IscsiTask {
+    int status;
+    int complete;
+    int retries;
+    int do_retry;
+    struct scsi_task *task;
+    Coroutine *co;
+} IscsiTask;
+
 typedef struct IscsiAIOCB {
     BlockDriverAIOCB common;
     QEMUIOVector *qiov;
@@ -72,6 +87,7 @@ typedef struct IscsiAIOCB {
 #define NOP_INTERVAL 5000
 #define MAX_NOP_FAILURES 3
 #define ISCSI_CMD_RETRIES 5
+#define ISCSI_MAX_UNMAP 131072
 
 static void
 iscsi_bh_cb(void *p)
@@ -105,6 +121,41 @@ iscsi_schedule_bh(IscsiAIOCB *acb)
     qemu_bh_schedule(acb->bh);
 }
 
+static void
+iscsi_co_generic_cb(struct iscsi_context *iscsi, int status,
+                        void *command_data, void *opaque)
+{
+    struct IscsiTask *iTask = opaque;
+    struct scsi_task *task = command_data;
+
+    iTask->complete = 1;
+    iTask->status = status;
+    iTask->do_retry = 0;
+    iTask->task = task;
+
+    if (iTask->retries-- > 0 && status == SCSI_STATUS_CHECK_CONDITION
+        && task->sense.key == SCSI_SENSE_UNIT_ATTENTION) {
+        iTask->do_retry = 1;
+        goto out;
+    }
+
+    if (status != SCSI_STATUS_GOOD) {
+        error_report("iSCSI: Failure. %s", iscsi_get_error(iscsi));
+    }
+
+out:
+    if (iTask->co) {
+        qemu_coroutine_enter(iTask->co, NULL);
+    }
+}
+
+static void iscsi_co_init_iscsitask(IscsiLun *iscsilun, struct IscsiTask *iTask)
+{
+    *iTask = (struct IscsiTask) {
+        .co         = qemu_coroutine_self(),
+        .retries    = ISCSI_CMD_RETRIES,
+    };
+}
 
 static void
 iscsi_abort_task_cb(struct iscsi_context *iscsi, int status, void *command_data,
@@ -568,88 +619,6 @@ iscsi_aio_flush(BlockDriverState *bs,
     return &acb->common;
 }
 
-static int iscsi_aio_discard_acb(IscsiAIOCB *acb);
-
-static void
-iscsi_unmap_cb(struct iscsi_context *iscsi, int status,
-                     void *command_data, void *opaque)
-{
-    IscsiAIOCB *acb = opaque;
-
-    if (acb->canceled != 0) {
-        return;
-    }
-
-    acb->status = 0;
-    if (status != 0) {
-        if (status == SCSI_STATUS_CHECK_CONDITION
-            && acb->task->sense.key == SCSI_SENSE_UNIT_ATTENTION
-            && acb->retries-- > 0) {
-            scsi_free_scsi_task(acb->task);
-            acb->task = NULL;
-            if (iscsi_aio_discard_acb(acb) == 0) {
-                iscsi_set_events(acb->iscsilun);
-                return;
-            }
-        }
-        error_report("Failed to unmap data on iSCSI lun. %s",
-                     iscsi_get_error(iscsi));
-        acb->status = -EIO;
-    }
-
-    iscsi_schedule_bh(acb);
-}
-
-static int iscsi_aio_discard_acb(IscsiAIOCB *acb) {
-    struct iscsi_context *iscsi = acb->iscsilun->iscsi;
-    struct unmap_list list[1];
-
-    acb->canceled   = 0;
-    acb->bh         = NULL;
-    acb->status     = -EINPROGRESS;
-    acb->buf        = NULL;
-
-    list[0].lba = sector_qemu2lun(acb->sector_num, acb->iscsilun);
-    list[0].num = acb->nb_sectors * BDRV_SECTOR_SIZE / acb->iscsilun->block_size;
-
-    acb->task = iscsi_unmap_task(iscsi, acb->iscsilun->lun,
-                                 0, 0, &list[0], 1,
-                                 iscsi_unmap_cb,
-                                 acb);
-    if (acb->task == NULL) {
-        error_report("iSCSI: Failed to send unmap command. %s",
-                     iscsi_get_error(iscsi));
-        return -1;
-    }
-
-    return 0;
-}
-
-static BlockDriverAIOCB *
-iscsi_aio_discard(BlockDriverState *bs,
-                  int64_t sector_num, int nb_sectors,
-                  BlockDriverCompletionFunc *cb, void *opaque)
-{
-    IscsiLun *iscsilun = bs->opaque;
-    IscsiAIOCB *acb;
-
-    acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque);
-
-    acb->iscsilun    = iscsilun;
-    acb->nb_sectors  = nb_sectors;
-    acb->sector_num  = sector_num;
-    acb->retries     = ISCSI_CMD_RETRIES;
-
-    if (iscsi_aio_discard_acb(acb) != 0) {
-        qemu_aio_release(acb);
-        return NULL;
-    }
-
-    iscsi_set_events(iscsilun);
-
-    return &acb->common;
-}
-
 #ifdef __linux__
 static void
 iscsi_aio_ioctl_cb(struct iscsi_context *iscsi, int status,
@@ -842,6 +811,171 @@ iscsi_getlength(BlockDriverState *bs)
     return len;
 }
 
+#if defined(SCSI_PROVISIONING_TYPE_DEALLOCATED)
+
+static int64_t coroutine_fn iscsi_co_get_block_status(BlockDriverState *bs,
+                                                  int64_t sector_num,
+                                                  int nb_sectors, int *pnum)
+{
+    IscsiLun *iscsilun = bs->opaque;
+    struct scsi_get_lba_status *lbas = NULL;
+    struct scsi_lba_status_descriptor *lbasd = NULL;
+    struct IscsiTask iTask;
+    int64_t ret;
+
+    iscsi_co_init_iscsitask(iscsilun, &iTask);
+
+    if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
+        ret = -EINVAL;
+        goto out;
+    }
+
+    /* default to all sectors allocated */
+    ret = BDRV_BLOCK_DATA;
+    ret |= (sector_num << BDRV_SECTOR_BITS) | BDRV_BLOCK_OFFSET_VALID;
+    *pnum = nb_sectors;
+
+    /* LUN does not support logical block provisioning */
+    if (iscsilun->lbpme == 0) {
+        goto out;
+    }
+
+retry:
+    if (iscsi_get_lba_status_task(iscsilun->iscsi, iscsilun->lun,
+                                  sector_qemu2lun(sector_num, iscsilun),
+                                  8 + 16, iscsi_co_generic_cb,
+                                  &iTask) == NULL) {
+        ret = -EIO;
+        goto out;
+    }
+
+    while (!iTask.complete) {
+        iscsi_set_events(iscsilun);
+        qemu_coroutine_yield();
+    }
+
+    if (iTask.do_retry) {
+        if (iTask.task != NULL) {
+            scsi_free_scsi_task(iTask.task);
+            iTask.task = NULL;
+        }
+        goto retry;
+    }
+
+    if (iTask.status != SCSI_STATUS_GOOD) {
+        /* in case the get_lba_status_callout fails (i.e.
+         * because the device is busy or the cmd is not
+         * supported) we pretend all blocks are allocated
+         * for backwards compatiblity */
+        goto out;
+    }
+
+    lbas = scsi_datain_unmarshall(iTask.task);
+    if (lbas == NULL) {
+        ret = -EIO;
+        goto out;
+    }
+
+    lbasd = &lbas->descriptors[0];
+
+    if (sector_qemu2lun(sector_num, iscsilun) != lbasd->lba) {
+        ret = -EIO;
+        goto out;
+    }
+
+    *pnum = sector_lun2qemu(lbasd->num_blocks, iscsilun);
+    if (*pnum > nb_sectors) {
+        *pnum = nb_sectors;
+    }
+
+    if (lbasd->provisioning == SCSI_PROVISIONING_TYPE_DEALLOCATED ||
+        lbasd->provisioning == SCSI_PROVISIONING_TYPE_ANCHORED) {
+        ret &= ~BDRV_BLOCK_DATA;
+        if (iscsilun->lbprz) {
+            ret |= BDRV_BLOCK_ZERO;
+        }
+    }
+
+out:
+    if (iTask.task != NULL) {
+        scsi_free_scsi_task(iTask.task);
+    }
+    return ret;
+}
+
+#endif /* SCSI_PROVISIONING_TYPE_DEALLOCATED */
+
+static int
+coroutine_fn iscsi_co_discard(BlockDriverState *bs, int64_t sector_num,
+                                   int nb_sectors)
+{
+    IscsiLun *iscsilun = bs->opaque;
+    struct IscsiTask iTask;
+    struct unmap_list list;
+    uint32_t nb_blocks;
+    uint32_t max_unmap;
+
+    if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
+        return -EINVAL;
+    }
+
+    if (!iscsilun->lbp.lbpu) {
+        /* UNMAP is not supported by the target */
+        return 0;
+    }
+
+    list.lba = sector_qemu2lun(sector_num, iscsilun);
+    nb_blocks = sector_qemu2lun(nb_sectors, iscsilun);
+
+    max_unmap = iscsilun->bl.max_unmap;
+    if (max_unmap == 0xffffffff) {
+        max_unmap = ISCSI_MAX_UNMAP;
+    }
+
+    while (nb_blocks > 0) {
+        iscsi_co_init_iscsitask(iscsilun, &iTask);
+        list.num = nb_blocks;
+        if (list.num > max_unmap) {
+            list.num = max_unmap;
+        }
+retry:
+        if (iscsi_unmap_task(iscsilun->iscsi, iscsilun->lun, 0, 0, &list, 1,
+                         iscsi_co_generic_cb, &iTask) == NULL) {
+            return -EIO;
+        }
+
+        while (!iTask.complete) {
+            iscsi_set_events(iscsilun);
+            qemu_coroutine_yield();
+        }
+
+        if (iTask.task != NULL) {
+            scsi_free_scsi_task(iTask.task);
+            iTask.task = NULL;
+        }
+
+        if (iTask.do_retry) {
+            goto retry;
+        }
+
+        if (iTask.status == SCSI_STATUS_CHECK_CONDITION) {
+            /* the target might fail with a check condition if it
+               is not happy with the alignment of the UNMAP request
+               we silently fail in this case */
+            return 0;
+        }
+
+        if (iTask.status != SCSI_STATUS_GOOD) {
+            return -EIO;
+        }
+
+        list.lba += list.num;
+        nb_blocks -= list.num;
+    }
+
+    return 0;
+}
+
 static int parse_chap(struct iscsi_context *iscsi, const char *target)
 {
     QemuOptsList *list;
@@ -922,8 +1056,9 @@ static char *parse_initiator_name(const char *target)
 {
     QemuOptsList *list;
     QemuOpts *opts;
-    const char *name = NULL;
-    const char *iscsi_name = qemu_get_vm_name();
+    const char *name;
+    char *iscsi_name;
+    UuidInfo *uuid_info;
 
     list = qemu_find_opts("iscsi");
     if (list) {
@@ -933,16 +1068,22 @@ static char *parse_initiator_name(const char *target)
         }
         if (opts) {
             name = qemu_opt_get(opts, "initiator-name");
+            if (name) {
+                return g_strdup(name);
+            }
         }
     }
 
-    if (name) {
-        return g_strdup(name);
+    uuid_info = qmp_query_uuid(NULL);
+    if (strcmp(uuid_info->UUID, UUID_NONE) == 0) {
+        name = qemu_get_vm_name();
     } else {
-        return g_strdup_printf("iqn.2008-11.org.linux-kvm%s%s",
-                               iscsi_name ? ":" : "",
-                               iscsi_name ? iscsi_name : "");
+        name = uuid_info->UUID;
     }
+    iscsi_name = g_strdup_printf("iqn.2008-11.org.linux-kvm%s%s",
+                                 name ? ":" : "", name ? name : "");
+    qapi_free_UuidInfo(uuid_info);
+    return iscsi_name;
 }
 
 #if defined(LIBISCSI_FEATURE_NOP_COUNTER)
@@ -990,6 +1131,8 @@ static int iscsi_readcapacity_sync(IscsiLun *iscsilun)
                 } else {
                     iscsilun->block_size = rc16->block_length;
                     iscsilun->num_blocks = rc16->returned_lba + 1;
+                    iscsilun->lbpme = rc16->lbpme;
+                    iscsilun->lbprz = rc16->lbprz;
                 }
             }
             break;
@@ -1042,11 +1185,43 @@ static QemuOptsList runtime_opts = {
     },
 };
 
+static struct scsi_task *iscsi_do_inquiry(struct iscsi_context *iscsi,
+                                          int lun, int evpd, int pc) {
+        int full_size;
+        struct scsi_task *task = NULL;
+        task = iscsi_inquiry_sync(iscsi, lun, evpd, pc, 64);
+        if (task == NULL || task->status != SCSI_STATUS_GOOD) {
+            goto fail;
+        }
+        full_size = scsi_datain_getfullsize(task);
+        if (full_size > task->datain.size) {
+            scsi_free_scsi_task(task);
+
+            /* we need more data for the full list */
+            task = iscsi_inquiry_sync(iscsi, lun, evpd, pc, full_size);
+            if (task == NULL || task->status != SCSI_STATUS_GOOD) {
+                goto fail;
+            }
+        }
+
+        return task;
+
+fail:
+        error_report("iSCSI: Inquiry command failed : %s",
+                     iscsi_get_error(iscsi));
+        if (task) {
+            scsi_free_scsi_task(task);
+            return NULL;
+        }
+        return NULL;
+}
+
 /*
  * We support iscsi url's on the form
  * iscsi://[<username>%<password>@]<host>[:<port>]/<targetname>/<lun>
  */
-static int iscsi_open(BlockDriverState *bs, QDict *options, int flags)
+static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
+                      Error **errp)
 {
     IscsiLun *iscsilun = bs->opaque;
     struct iscsi_context *iscsi = NULL;
@@ -1171,6 +1346,46 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags)
         bs->sg = 1;
     }
 
+    if (iscsilun->lbpme) {
+        struct scsi_inquiry_logical_block_provisioning *inq_lbp;
+        task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1,
+                                SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING);
+        if (task == NULL) {
+            ret = -EINVAL;
+            goto out;
+        }
+        inq_lbp = scsi_datain_unmarshall(task);
+        if (inq_lbp == NULL) {
+            error_report("iSCSI: failed to unmarshall inquiry datain blob");
+            ret = -EINVAL;
+            goto out;
+        }
+        memcpy(&iscsilun->lbp, inq_lbp,
+               sizeof(struct scsi_inquiry_logical_block_provisioning));
+        scsi_free_scsi_task(task);
+        task = NULL;
+    }
+
+    if (iscsilun->lbp.lbpu || iscsilun->lbp.lbpws) {
+        struct scsi_inquiry_block_limits *inq_bl;
+        task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1,
+                                SCSI_INQUIRY_PAGECODE_BLOCK_LIMITS);
+        if (task == NULL) {
+            ret = -EINVAL;
+            goto out;
+        }
+        inq_bl = scsi_datain_unmarshall(task);
+        if (inq_bl == NULL) {
+            error_report("iSCSI: failed to unmarshall inquiry datain blob");
+            ret = -EINVAL;
+            goto out;
+        }
+        memcpy(&iscsilun->bl, inq_bl,
+               sizeof(struct scsi_inquiry_block_limits));
+        scsi_free_scsi_task(task);
+        task = NULL;
+    }
+
 #if defined(LIBISCSI_FEATURE_NOP_COUNTER)
     /* Set up a timer for sending out iSCSI NOPs */
     iscsilun->nop_timer = timer_new_ms(QEMU_CLOCK_REALTIME, iscsi_nop_timed_event, iscsilun);
@@ -1237,7 +1452,8 @@ static int iscsi_has_zero_init(BlockDriverState *bs)
     return 0;
 }
 
-static int iscsi_create(const char *filename, QEMUOptionParameter *options)
+static int iscsi_create(const char *filename, QEMUOptionParameter *options,
+                        Error **errp)
 {
     int ret = 0;
     int64_t total_size = 0;
@@ -1260,7 +1476,7 @@ static int iscsi_create(const char *filename, QEMUOptionParameter *options)
 
     bs_options = qdict_new();
     qdict_put(bs_options, "filename", qstring_from_str(filename));
-    ret = iscsi_open(bs, bs_options, 0);
+    ret = iscsi_open(bs, bs_options, 0, NULL);
     QDECREF(bs_options);
 
     if (ret != 0) {
@@ -1312,11 +1528,15 @@ static BlockDriver bdrv_iscsi = {
     .bdrv_getlength  = iscsi_getlength,
     .bdrv_truncate   = iscsi_truncate,
 
+#if defined(SCSI_PROVISIONING_TYPE_DEALLOCATED)
+    .bdrv_co_get_block_status = iscsi_co_get_block_status,
+#endif
+    .bdrv_co_discard      = iscsi_co_discard,
+
     .bdrv_aio_readv  = iscsi_aio_readv,
     .bdrv_aio_writev = iscsi_aio_writev,
     .bdrv_aio_flush  = iscsi_aio_flush,
 
-    .bdrv_aio_discard = iscsi_aio_discard,
     .bdrv_has_zero_init = iscsi_has_zero_init,
 
 #ifdef __linux__
index f61a7799def5bcca14878a4e3e8fe873eea6d596..6e7a274e43528a3df54a7d904ffd3d7c5f60c041 100644 (file)
@@ -505,14 +505,15 @@ static void mirror_iostatus_reset(BlockJob *job)
 static void mirror_complete(BlockJob *job, Error **errp)
 {
     MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
+    Error *local_err = NULL;
     int ret;
 
-    ret = bdrv_open_backing_file(s->target, NULL);
+    ret = bdrv_open_backing_file(s->target, NULL, &local_err);
     if (ret < 0) {
         char backing_filename[PATH_MAX];
         bdrv_get_full_backing_filename(s->target, backing_filename,
                                        sizeof(backing_filename));
-        error_setg_file_open(errp, -ret, backing_filename);
+        error_propagate(errp, local_err);
         return;
     }
     if (!s->synced) {
index 691066f726366e7b726a4094e14aa72f09983840..c8deeee67fb61ee0372176d2c869597c399956f1 100644 (file)
@@ -453,7 +453,8 @@ static void nbd_teardown_connection(BlockDriverState *bs)
     closesocket(s->sock);
 }
 
-static int nbd_open(BlockDriverState *bs, QDict *options, int flags)
+static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
+                    Error **errp)
 {
     BDRVNBDState *s = bs->opaque;
     int result;
index 18b3ac0b28abb6f226f7100efc7ad4c2998606c7..2121e43204558051f661dbdc9f7b7acd787922e2 100644 (file)
@@ -68,7 +68,8 @@ static int parallels_probe(const uint8_t *buf, int buf_size, const char *filenam
     return 0;
 }
 
-static int parallels_open(BlockDriverState *bs, QDict *options, int flags)
+static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
+                          Error **errp)
 {
     BDRVParallelsState *s = bs->opaque;
     int i;
index 93a993bb4426b6ceee44af01079d786c92d547c3..c470e05f60f0f1b2411416a7a3605dcd4e4ace39 100644 (file)
@@ -92,7 +92,8 @@ static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename)
         return 0;
 }
 
-static int qcow_open(BlockDriverState *bs, QDict *options, int flags)
+static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
+                     Error **errp)
 {
     BDRVQcowState *s = bs->opaque;
     int len, i, shift, ret;
@@ -658,7 +659,8 @@ static void qcow_close(BlockDriverState *bs)
     error_free(s->migration_blocker);
 }
 
-static int qcow_create(const char *filename, QEMUOptionParameter *options)
+static int qcow_create(const char *filename, QEMUOptionParameter *options,
+                       Error **errp)
 {
     int header_size, backing_filename_len, l1_size, shift, i;
     QCowHeader header;
@@ -666,6 +668,7 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options)
     int64_t total_size = 0;
     const char *backing_file = NULL;
     int flags = 0;
+    Error *local_err = NULL;
     int ret;
     BlockDriverState *qcow_bs;
 
@@ -681,13 +684,17 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options)
         options++;
     }
 
-    ret = bdrv_create_file(filename, options);
+    ret = bdrv_create_file(filename, options, &local_err);
     if (ret < 0) {
+        qerror_report_err(local_err);
+        error_free(local_err);
         return ret;
     }
 
-    ret = bdrv_file_open(&qcow_bs, filename, NULL, BDRV_O_RDWR);
+    ret = bdrv_file_open(&qcow_bs, filename, NULL, BDRV_O_RDWR, &local_err);
     if (ret < 0) {
+        qerror_report_err(local_err);
+        error_free(local_err);
         return ret;
     }
 
index 7bcae09a69f45f044784fb2a411f401a2bbae1e2..40a5a3fc39cb5d729a5c263dcb5b48c5c89f576a 100644 (file)
@@ -202,6 +202,24 @@ void qcow2_cache_depends_on_flush(Qcow2Cache *c)
     c->depends_on_flush = true;
 }
 
+int qcow2_cache_empty(BlockDriverState *bs, Qcow2Cache *c)
+{
+    int ret, i;
+
+    ret = qcow2_cache_flush(bs, c);
+    if (ret < 0) {
+        return ret;
+    }
+
+    for (i = 0; i < c->size; i++) {
+        assert(c->entries[i].ref == 0);
+        c->entries[i].offset = 0;
+        c->entries[i].cache_hits = 0;
+    }
+
+    return 0;
+}
+
 static int qcow2_cache_find_entry_to_replace(Qcow2Cache *c)
 {
     int i;
index 2d5aa92962de76c565150d917c7181298e1c4ceb..738ff73c1d0be9bb3800769bf82cb4a8a56675a7 100644 (file)
@@ -1338,7 +1338,7 @@ int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset)
  * clusters.
  */
 static int discard_single_l2(BlockDriverState *bs, uint64_t offset,
-    unsigned int nb_clusters)
+    unsigned int nb_clusters, enum qcow2_discard_type type)
 {
     BDRVQcowState *s = bs->opaque;
     uint64_t *l2_table;
@@ -1367,7 +1367,7 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset,
         l2_table[l2_index + i] = cpu_to_be64(0);
 
         /* Then decrease the refcount */
-        qcow2_free_any_clusters(bs, old_offset, 1, QCOW2_DISCARD_REQUEST);
+        qcow2_free_any_clusters(bs, old_offset, 1, type);
     }
 
     ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
@@ -1379,7 +1379,7 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset,
 }
 
 int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset,
-    int nb_sectors)
+    int nb_sectors, enum qcow2_discard_type type)
 {
     BDRVQcowState *s = bs->opaque;
     uint64_t end_offset;
@@ -1402,7 +1402,7 @@ int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset,
 
     /* Each L2 table is handled by its own loop iteration */
     while (nb_clusters > 0) {
-        ret = discard_single_l2(bs, offset, nb_clusters);
+        ret = discard_single_l2(bs, offset, nb_clusters, type);
         if (ret < 0) {
             goto fail;
         }
@@ -1497,3 +1497,236 @@ fail:
 
     return ret;
 }
+
+/*
+ * Expands all zero clusters in a specific L1 table (or deallocates them, for
+ * non-backed non-pre-allocated zero clusters).
+ *
+ * expanded_clusters is a bitmap where every bit corresponds to one cluster in
+ * the image file; a bit gets set if the corresponding cluster has been used for
+ * zero expansion (i.e., has been filled with zeroes and is referenced from an
+ * L2 table). nb_clusters contains the total cluster count of the image file,
+ * i.e., the number of bits in expanded_clusters.
+ */
+static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
+                                      int l1_size, uint8_t *expanded_clusters,
+                                      uint64_t nb_clusters)
+{
+    BDRVQcowState *s = bs->opaque;
+    bool is_active_l1 = (l1_table == s->l1_table);
+    uint64_t *l2_table = NULL;
+    int ret;
+    int i, j;
+
+    if (!is_active_l1) {
+        /* inactive L2 tables require a buffer to be stored in when loading
+         * them from disk */
+        l2_table = qemu_blockalign(bs, s->cluster_size);
+    }
+
+    for (i = 0; i < l1_size; i++) {
+        uint64_t l2_offset = l1_table[i] & L1E_OFFSET_MASK;
+        bool l2_dirty = false;
+
+        if (!l2_offset) {
+            /* unallocated */
+            continue;
+        }
+
+        if (is_active_l1) {
+            /* get active L2 tables from cache */
+            ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset,
+                    (void **)&l2_table);
+        } else {
+            /* load inactive L2 tables from disk */
+            ret = bdrv_read(bs->file, l2_offset / BDRV_SECTOR_SIZE,
+                    (void *)l2_table, s->cluster_sectors);
+        }
+        if (ret < 0) {
+            goto fail;
+        }
+
+        for (j = 0; j < s->l2_size; j++) {
+            uint64_t l2_entry = be64_to_cpu(l2_table[j]);
+            int64_t offset = l2_entry & L2E_OFFSET_MASK, cluster_index;
+            int cluster_type = qcow2_get_cluster_type(l2_entry);
+
+            if (cluster_type == QCOW2_CLUSTER_NORMAL) {
+                cluster_index = offset >> s->cluster_bits;
+                assert((cluster_index >= 0) && (cluster_index < nb_clusters));
+                if (expanded_clusters[cluster_index / 8] &
+                    (1 << (cluster_index % 8))) {
+                    /* Probably a shared L2 table; this cluster was a zero
+                     * cluster which has been expanded, its refcount
+                     * therefore most likely requires an update. */
+                    ret = qcow2_update_cluster_refcount(bs, cluster_index, 1,
+                                                        QCOW2_DISCARD_NEVER);
+                    if (ret < 0) {
+                        goto fail;
+                    }
+                    /* Since we just increased the refcount, the COPIED flag may
+                     * no longer be set. */
+                    l2_table[j] = cpu_to_be64(l2_entry & ~QCOW_OFLAG_COPIED);
+                    l2_dirty = true;
+                }
+                continue;
+            }
+            else if (qcow2_get_cluster_type(l2_entry) != QCOW2_CLUSTER_ZERO) {
+                continue;
+            }
+
+            if (!offset) {
+                /* not preallocated */
+                if (!bs->backing_hd) {
+                    /* not backed; therefore we can simply deallocate the
+                     * cluster */
+                    l2_table[j] = 0;
+                    l2_dirty = true;
+                    continue;
+                }
+
+                offset = qcow2_alloc_clusters(bs, s->cluster_size);
+                if (offset < 0) {
+                    ret = offset;
+                    goto fail;
+                }
+            }
+
+            ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT,
+                                                offset, s->cluster_size);
+            if (ret < 0) {
+                qcow2_free_clusters(bs, offset, s->cluster_size,
+                        QCOW2_DISCARD_ALWAYS);
+                goto fail;
+            }
+
+            ret = bdrv_write_zeroes(bs->file, offset / BDRV_SECTOR_SIZE,
+                                    s->cluster_sectors);
+            if (ret < 0) {
+                qcow2_free_clusters(bs, offset, s->cluster_size,
+                        QCOW2_DISCARD_ALWAYS);
+                goto fail;
+            }
+
+            l2_table[j] = cpu_to_be64(offset | QCOW_OFLAG_COPIED);
+            l2_dirty = true;
+
+            cluster_index = offset >> s->cluster_bits;
+            assert((cluster_index >= 0) && (cluster_index < nb_clusters));
+            expanded_clusters[cluster_index / 8] |= 1 << (cluster_index % 8);
+        }
+
+        if (is_active_l1) {
+            if (l2_dirty) {
+                qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table);
+                qcow2_cache_depends_on_flush(s->l2_table_cache);
+            }
+            ret = qcow2_cache_put(bs, s->l2_table_cache, (void **)&l2_table);
+            if (ret < 0) {
+                l2_table = NULL;
+                goto fail;
+            }
+        } else {
+            if (l2_dirty) {
+                ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT &
+                        ~(QCOW2_OL_INACTIVE_L2 | QCOW2_OL_ACTIVE_L2), l2_offset,
+                        s->cluster_size);
+                if (ret < 0) {
+                    goto fail;
+                }
+
+                ret = bdrv_write(bs->file, l2_offset / BDRV_SECTOR_SIZE,
+                        (void *)l2_table, s->cluster_sectors);
+                if (ret < 0) {
+                    goto fail;
+                }
+            }
+        }
+    }
+
+    ret = 0;
+
+fail:
+    if (l2_table) {
+        if (!is_active_l1) {
+            qemu_vfree(l2_table);
+        } else {
+            if (ret < 0) {
+                qcow2_cache_put(bs, s->l2_table_cache, (void **)&l2_table);
+            } else {
+                ret = qcow2_cache_put(bs, s->l2_table_cache,
+                        (void **)&l2_table);
+            }
+        }
+    }
+    return ret;
+}
+
+/*
+ * For backed images, expands all zero clusters on the image. For non-backed
+ * images, deallocates all non-pre-allocated zero clusters (and claims the
+ * allocation for pre-allocated ones). This is important for downgrading to a
+ * qcow2 version which doesn't yet support metadata zero clusters.
+ */
+int qcow2_expand_zero_clusters(BlockDriverState *bs)
+{
+    BDRVQcowState *s = bs->opaque;
+    uint64_t *l1_table = NULL;
+    int cluster_to_sector_bits = s->cluster_bits - BDRV_SECTOR_BITS;
+    uint64_t nb_clusters;
+    uint8_t *expanded_clusters;
+    int ret;
+    int i, j;
+
+    nb_clusters = (bs->total_sectors + (1 << cluster_to_sector_bits) - 1)
+            >> cluster_to_sector_bits;
+    expanded_clusters = g_malloc0((nb_clusters + 7) / 8);
+
+    ret = expand_zero_clusters_in_l1(bs, s->l1_table, s->l1_size,
+                                     expanded_clusters, nb_clusters);
+    if (ret < 0) {
+        goto fail;
+    }
+
+    /* Inactive L1 tables may point to active L2 tables - therefore it is
+     * necessary to flush the L2 table cache before trying to access the L2
+     * tables pointed to by inactive L1 entries (else we might try to expand
+     * zero clusters that have already been expanded); furthermore, it is also
+     * necessary to empty the L2 table cache, since it may contain tables which
+     * are now going to be modified directly on disk, bypassing the cache.
+     * qcow2_cache_empty() does both for us. */
+    ret = qcow2_cache_empty(bs, s->l2_table_cache);
+    if (ret < 0) {
+        goto fail;
+    }
+
+    for (i = 0; i < s->nb_snapshots; i++) {
+        int l1_sectors = (s->snapshots[i].l1_size * sizeof(uint64_t) +
+                BDRV_SECTOR_SIZE - 1) / BDRV_SECTOR_SIZE;
+
+        l1_table = g_realloc(l1_table, l1_sectors * BDRV_SECTOR_SIZE);
+
+        ret = bdrv_read(bs->file, s->snapshots[i].l1_table_offset /
+                BDRV_SECTOR_SIZE, (void *)l1_table, l1_sectors);
+        if (ret < 0) {
+            goto fail;
+        }
+
+        for (j = 0; j < s->snapshots[i].l1_size; j++) {
+            be64_to_cpus(&l1_table[j]);
+        }
+
+        ret = expand_zero_clusters_in_l1(bs, l1_table, s->snapshots[i].l1_size,
+                                         expanded_clusters, nb_clusters);
+        if (ret < 0) {
+            goto fail;
+        }
+    }
+
+    ret = 0;
+
+fail:
+    g_free(expanded_clusters);
+    g_free(l1_table);
+    return ret;
+}
index ba129de478fcaaa4b27aa71fb727ae6b2c4dd168..426414814264d52d381a0d9574a7769aba13e836 100644 (file)
@@ -601,10 +601,10 @@ fail:
  * If the return value is non-negative, it is the new refcount of the cluster.
  * If it is negative, it is -errno and indicates an error.
  */
-static int update_cluster_refcount(BlockDriverState *bs,
-                                   int64_t cluster_index,
-                                   int addend,
-                                   enum qcow2_discard_type type)
+int qcow2_update_cluster_refcount(BlockDriverState *bs,
+                                  int64_t cluster_index,
+                                  int addend,
+                                  enum qcow2_discard_type type)
 {
     BDRVQcowState *s = bs->opaque;
     int ret;
@@ -733,8 +733,8 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)
         if (free_in_cluster == 0)
             s->free_byte_offset = 0;
         if ((offset & (s->cluster_size - 1)) != 0)
-            update_cluster_refcount(bs, offset >> s->cluster_bits, 1,
-                                    QCOW2_DISCARD_NEVER);
+            qcow2_update_cluster_refcount(bs, offset >> s->cluster_bits, 1,
+                                          QCOW2_DISCARD_NEVER);
     } else {
         offset = qcow2_alloc_clusters(bs, s->cluster_size);
         if (offset < 0) {
@@ -744,8 +744,8 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)
         if ((cluster_offset + s->cluster_size) == offset) {
             /* we are lucky: contiguous data */
             offset = s->free_byte_offset;
-            update_cluster_refcount(bs, offset >> s->cluster_bits, 1,
-                                    QCOW2_DISCARD_NEVER);
+            qcow2_update_cluster_refcount(bs, offset >> s->cluster_bits, 1,
+                                          QCOW2_DISCARD_NEVER);
             s->free_byte_offset += size;
         } else {
             s->free_byte_offset = offset;
@@ -754,8 +754,8 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)
     }
 
     /* The cluster refcount was incremented, either by qcow2_alloc_clusters()
-     * or explicitly by update_cluster_refcount().  Refcount blocks must be
-     * flushed before the caller's L2 table updates.
+     * or explicitly by qcow2_update_cluster_refcount().  Refcount blocks must
+     * be flushed before the caller's L2 table updates.
      */
     qcow2_cache_set_dependency(bs, s->l2_table_cache, s->refcount_block_cache);
     return offset;
@@ -896,8 +896,9 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
                             break;
                         }
                         if (addend != 0) {
-                            refcount = update_cluster_refcount(bs, cluster_index, addend,
-                                                               QCOW2_DISCARD_SNAPSHOT);
+                            refcount = qcow2_update_cluster_refcount(bs,
+                                    cluster_index, addend,
+                                    QCOW2_DISCARD_SNAPSHOT);
                         } else {
                             refcount = get_refcount(bs, cluster_index);
                         }
@@ -936,8 +937,8 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
 
 
             if (addend != 0) {
-                refcount = update_cluster_refcount(bs, l2_offset >> s->cluster_bits, addend,
-                                                   QCOW2_DISCARD_SNAPSHOT);
+                refcount = qcow2_update_cluster_refcount(bs, l2_offset >>
+                        s->cluster_bits, addend, QCOW2_DISCARD_SNAPSHOT);
             } else {
                 refcount = get_refcount(bs, l2_offset >> s->cluster_bits);
             }
index e7e601301a9467b6c5ebad7cec0f3431bbb75999..7d144205c3fdd65bae8a728e2f9d7ad54bb6bdc7 100644 (file)
@@ -297,31 +297,47 @@ static void find_new_snapshot_id(BlockDriverState *bs,
     snprintf(id_str, id_str_size, "%d", id_max + 1);
 }
 
-static int find_snapshot_by_id(BlockDriverState *bs, const char *id_str)
+static int find_snapshot_by_id_and_name(BlockDriverState *bs,
+                                        const char *id,
+                                        const char *name)
 {
     BDRVQcowState *s = bs->opaque;
     int i;
 
-    for(i = 0; i < s->nb_snapshots; i++) {
-        if (!strcmp(s->snapshots[i].id_str, id_str))
-            return i;
+    if (id && name) {
+        for (i = 0; i < s->nb_snapshots; i++) {
+            if (!strcmp(s->snapshots[i].id_str, id) &&
+                !strcmp(s->snapshots[i].name, name)) {
+                return i;
+            }
+        }
+    } else if (id) {
+        for (i = 0; i < s->nb_snapshots; i++) {
+            if (!strcmp(s->snapshots[i].id_str, id)) {
+                return i;
+            }
+        }
+    } else if (name) {
+        for (i = 0; i < s->nb_snapshots; i++) {
+            if (!strcmp(s->snapshots[i].name, name)) {
+                return i;
+            }
+        }
     }
+
     return -1;
 }
 
-static int find_snapshot_by_id_or_name(BlockDriverState *bs, const char *name)
+static int find_snapshot_by_id_or_name(BlockDriverState *bs,
+                                       const char *id_or_name)
 {
-    BDRVQcowState *s = bs->opaque;
-    int i, ret;
+    int ret;
 
-    ret = find_snapshot_by_id(bs, name);
-    if (ret >= 0)
+    ret = find_snapshot_by_id_and_name(bs, id_or_name, NULL);
+    if (ret >= 0) {
         return ret;
-    for(i = 0; i < s->nb_snapshots; i++) {
-        if (!strcmp(s->snapshots[i].name, name))
-            return i;
     }
-    return -1;
+    return find_snapshot_by_id_and_name(bs, NULL, id_or_name);
 }
 
 /* if no id is provided, a new one is constructed */
@@ -343,7 +359,7 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
     }
 
     /* Check that the ID is unique */
-    if (find_snapshot_by_id(bs, sn_info->id_str) >= 0) {
+    if (find_snapshot_by_id_and_name(bs, sn_info->id_str, NULL) >= 0) {
         return -EEXIST;
     }
 
@@ -416,6 +432,13 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
 
     g_free(old_snapshot_list);
 
+    /* The VM state isn't needed any more in the active L1 table; in fact, it
+     * hurts by causing expensive COW for the next snapshot. */
+    qcow2_discard_clusters(bs, qcow2_vm_state_offset(s),
+                           align_offset(sn->vm_state_size, s->cluster_size)
+                                >> BDRV_SECTOR_BITS,
+                           QCOW2_DISCARD_NEVER);
+
 #ifdef DEBUG_ALLOC
     {
       BdrvCheckResult result = {0};
@@ -553,15 +576,19 @@ fail:
     return ret;
 }
 
-int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id)
+int qcow2_snapshot_delete(BlockDriverState *bs,
+                          const char *snapshot_id,
+                          const char *name,
+                          Error **errp)
 {
     BDRVQcowState *s = bs->opaque;
     QCowSnapshot sn;
     int snapshot_index, ret;
 
     /* Search the snapshot */
-    snapshot_index = find_snapshot_by_id_or_name(bs, snapshot_id);
+    snapshot_index = find_snapshot_by_id_and_name(bs, snapshot_id, name);
     if (snapshot_index < 0) {
+        error_setg(errp, "Can't find the snapshot");
         return -ENOENT;
     }
     sn = s->snapshots[snapshot_index];
@@ -573,6 +600,7 @@ int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id)
     s->nb_snapshots--;
     ret = qcow2_write_snapshots(bs);
     if (ret < 0) {
+        error_setg(errp, "Failed to remove snapshot from snapshot list");
         return ret;
     }
 
@@ -590,6 +618,7 @@ int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id)
     ret = qcow2_update_snapshot_refcount(bs, sn.l1_table_offset,
                                          sn.l1_size, -1);
     if (ret < 0) {
+        error_setg(errp, "Failed to free the cluster and L1 table");
         return ret;
     }
     qcow2_free_clusters(bs, sn.l1_table_offset, sn.l1_size * sizeof(uint64_t),
@@ -598,6 +627,7 @@ int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id)
     /* must update the copied flag on the current cluster offsets */
     ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 0);
     if (ret < 0) {
+        error_setg(errp, "Failed to update snapshot status in disk");
         return ret;
     }
 
index 578792f0a37dae59b04f9dcb239ec561696b6d1e..318d95d972ef4f0d20e126ad3b44f22a895ca5bb 100644 (file)
@@ -79,7 +79,8 @@ static int qcow2_probe(const uint8_t *buf, int buf_size, const char *filename)
  * return 0 upon success, non-0 otherwise
  */
 static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
-                                 uint64_t end_offset, void **p_feature_table)
+                                 uint64_t end_offset, void **p_feature_table,
+                                 Error **errp)
 {
     BDRVQcowState *s = bs->opaque;
     QCowExtension ext;
@@ -100,10 +101,10 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
         printf("attempting to read extended header in offset %lu\n", offset);
 #endif
 
-        if (bdrv_pread(bs->file, offset, &ext, sizeof(ext)) != sizeof(ext)) {
-            fprintf(stderr, "qcow2_read_extension: ERROR: "
-                    "pread fail from offset %" PRIu64 "\n",
-                    offset);
+        ret = bdrv_pread(bs->file, offset, &ext, sizeof(ext));
+        if (ret < 0) {
+            error_setg_errno(errp, -ret, "qcow2_read_extension: ERROR: "
+                             "pread fail from offset %" PRIu64, offset);
             return 1;
         }
         be32_to_cpus(&ext.magic);
@@ -113,7 +114,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
         printf("ext.magic = 0x%x\n", ext.magic);
 #endif
         if (ext.len > end_offset - offset) {
-            error_report("Header extension too large");
+            error_setg(errp, "Header extension too large");
             return -EINVAL;
         }
 
@@ -123,14 +124,16 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
 
         case QCOW2_EXT_MAGIC_BACKING_FORMAT:
             if (ext.len >= sizeof(bs->backing_format)) {
-                fprintf(stderr, "ERROR: ext_backing_format: len=%u too large"
-                        " (>=%zu)\n",
-                        ext.len, sizeof(bs->backing_format));
+                error_setg(errp, "ERROR: ext_backing_format: len=%u too large"
+                           " (>=%zu)", ext.len, sizeof(bs->backing_format));
                 return 2;
             }
-            if (bdrv_pread(bs->file, offset , bs->backing_format,
-                           ext.len) != ext.len)
+            ret = bdrv_pread(bs->file, offset, bs->backing_format, ext.len);
+            if (ret < 0) {
+                error_setg_errno(errp, -ret, "ERROR: ext_backing_format: "
+                                 "Could not read format name");
                 return 3;
+            }
             bs->backing_format[ext.len] = '\0';
 #ifdef DEBUG_EXT
             printf("Qcow2: Got format extension %s\n", bs->backing_format);
@@ -142,6 +145,8 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
                 void* feature_table = g_malloc0(ext.len + 2 * sizeof(Qcow2Feature));
                 ret = bdrv_pread(bs->file, offset , feature_table, ext.len);
                 if (ret < 0) {
+                    error_setg_errno(errp, -ret, "ERROR: ext_feature_table: "
+                                     "Could not read table");
                     return ret;
                 }
 
@@ -161,6 +166,8 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
 
                 ret = bdrv_pread(bs->file, offset , uext->data, uext->len);
                 if (ret < 0) {
+                    error_setg_errno(errp, -ret, "ERROR: unknown extension: "
+                                     "Could not read data");
                     return ret;
                 }
             }
@@ -184,8 +191,8 @@ static void cleanup_unknown_header_ext(BlockDriverState *bs)
     }
 }
 
-static void GCC_FMT_ATTR(2, 3) report_unsupported(BlockDriverState *bs,
-    const char *fmt, ...)
+static void GCC_FMT_ATTR(3, 4) report_unsupported(BlockDriverState *bs,
+    Error **errp, const char *fmt, ...)
 {
     char msg[64];
     va_list ap;
@@ -194,17 +201,17 @@ static void GCC_FMT_ATTR(2, 3) report_unsupported(BlockDriverState *bs,
     vsnprintf(msg, sizeof(msg), fmt, ap);
     va_end(ap);
 
-    qerror_report(QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
-        bs->device_name, "qcow2", msg);
+    error_set(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, bs->device_name, "qcow2",
+              msg);
 }
 
 static void report_unsupported_feature(BlockDriverState *bs,
-    Qcow2Feature *table, uint64_t mask)
+    Error **errp, Qcow2Feature *table, uint64_t mask)
 {
     while (table && table->name[0] != '\0') {
         if (table->type == QCOW2_FEAT_TYPE_INCOMPATIBLE) {
             if (mask & (1 << table->bit)) {
-                report_unsupported(bs, "%.46s",table->name);
+                report_unsupported(bs, errp, "%.46s", table->name);
                 mask &= ~(1 << table->bit);
             }
         }
@@ -212,7 +219,8 @@ static void report_unsupported_feature(BlockDriverState *bs,
     }
 
     if (mask) {
-        report_unsupported(bs, "Unknown incompatible feature: %" PRIx64, mask);
+        report_unsupported(bs, errp, "Unknown incompatible feature: %" PRIx64,
+                           mask);
     }
 }
 
@@ -350,7 +358,8 @@ static QemuOptsList qcow2_runtime_opts = {
     },
 };
 
-static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
+static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
+                      Error **errp)
 {
     BDRVQcowState *s = bs->opaque;
     int len, i, ret = 0;
@@ -362,6 +371,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
 
     ret = bdrv_pread(bs->file, 0, &header, sizeof(header));
     if (ret < 0) {
+        error_setg_errno(errp, -ret, "Could not read qcow2 header");
         goto fail;
     }
     be32_to_cpus(&header.magic);
@@ -379,11 +389,12 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
     be32_to_cpus(&header.nb_snapshots);
 
     if (header.magic != QCOW_MAGIC) {
+        error_setg(errp, "Image is not in qcow2 format");
         ret = -EMEDIUMTYPE;
         goto fail;
     }
     if (header.version < 2 || header.version > 3) {
-        report_unsupported(bs, "QCOW version %d", header.version);
+        report_unsupported(bs, errp, "QCOW version %d", header.version);
         ret = -ENOTSUP;
         goto fail;
     }
@@ -411,6 +422,8 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
         ret = bdrv_pread(bs->file, sizeof(header), s->unknown_header_fields,
                          s->unknown_header_fields_size);
         if (ret < 0) {
+            error_setg_errno(errp, -ret, "Could not read unknown qcow2 header "
+                             "fields");
             goto fail;
         }
     }
@@ -429,8 +442,8 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
     if (s->incompatible_features & ~QCOW2_INCOMPAT_MASK) {
         void *feature_table = NULL;
         qcow2_read_extensions(bs, header.header_length, ext_end,
-                              &feature_table);
-        report_unsupported_feature(bs, feature_table,
+                              &feature_table, NULL);
+        report_unsupported_feature(bs, errp, feature_table,
                                    s->incompatible_features &
                                    ~QCOW2_INCOMPAT_MASK);
         ret = -ENOTSUP;
@@ -441,8 +454,8 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
         /* Corrupt images may not be written to unless they are being repaired
          */
         if ((flags & BDRV_O_RDWR) && !(flags & BDRV_O_CHECK)) {
-            error_report("qcow2: Image is corrupt; cannot be opened "
-                    "read/write.");
+            error_setg(errp, "qcow2: Image is corrupt; cannot be opened "
+                       "read/write");
             ret = -EACCES;
             goto fail;
         }
@@ -450,18 +463,22 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
 
     /* Check support for various header values */
     if (header.refcount_order != 4) {
-        report_unsupported(bs, "%d bit reference counts",
+        report_unsupported(bs, errp, "%d bit reference counts",
                            1 << header.refcount_order);
         ret = -ENOTSUP;
         goto fail;
     }
+    s->refcount_order = header.refcount_order;
 
     if (header.cluster_bits < MIN_CLUSTER_BITS ||
         header.cluster_bits > MAX_CLUSTER_BITS) {
+        error_setg(errp, "Unsupported cluster size: 2^%i", header.cluster_bits);
         ret = -EINVAL;
         goto fail;
     }
     if (header.crypt_method > QCOW_CRYPT_AES) {
+        error_setg(errp, "Unsupported encryption method: %i",
+                   header.crypt_method);
         ret = -EINVAL;
         goto fail;
     }
@@ -490,6 +507,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
 
     l1_vm_state_index = size_to_l1(s, header.size);
     if (l1_vm_state_index > INT_MAX) {
+        error_setg(errp, "Image is too big");
         ret = -EFBIG;
         goto fail;
     }
@@ -498,6 +516,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
     /* the L1 table must contain at least enough entries to put
        header.size bytes */
     if (s->l1_size < s->l1_vm_state_index) {
+        error_setg(errp, "L1 table is too small");
         ret = -EINVAL;
         goto fail;
     }
@@ -508,6 +527,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
         ret = bdrv_pread(bs->file, s->l1_table_offset, s->l1_table,
                          s->l1_size * sizeof(uint64_t));
         if (ret < 0) {
+            error_setg_errno(errp, -ret, "Could not read L1 table");
             goto fail;
         }
         for(i = 0;i < s->l1_size; i++) {
@@ -528,6 +548,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
 
     ret = qcow2_refcount_init(bs);
     if (ret != 0) {
+        error_setg_errno(errp, -ret, "Could not initialize refcount handling");
         goto fail;
     }
 
@@ -535,7 +556,9 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
     QTAILQ_INIT(&s->discards);
 
     /* read qcow2 extensions */
-    if (qcow2_read_extensions(bs, header.header_length, ext_end, NULL)) {
+    if (qcow2_read_extensions(bs, header.header_length, ext_end, NULL,
+        &local_err)) {
+        error_propagate(errp, local_err);
         ret = -EINVAL;
         goto fail;
     }
@@ -549,6 +572,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
         ret = bdrv_pread(bs->file, header.backing_file_offset,
                          bs->backing_file, len);
         if (ret < 0) {
+            error_setg_errno(errp, -ret, "Could not read backing file name");
             goto fail;
         }
         bs->backing_file[len] = '\0';
@@ -556,6 +580,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
 
     ret = qcow2_read_snapshots(bs);
     if (ret < 0) {
+        error_setg_errno(errp, -ret, "Could not read snapshots");
         goto fail;
     }
 
@@ -564,6 +589,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
         s->autoclear_features = 0;
         ret = qcow2_update_header(bs);
         if (ret < 0) {
+            error_setg_errno(errp, -ret, "Could not update qcow2 header");
             goto fail;
         }
     }
@@ -578,6 +604,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
 
         ret = qcow2_check(bs, &result, BDRV_FIX_ERRORS);
         if (ret < 0) {
+            error_setg_errno(errp, -ret, "Could not repair dirty image");
             goto fail;
         }
     }
@@ -586,8 +613,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
     opts = qemu_opts_create_nofail(&qcow2_runtime_opts);
     qemu_opts_absorb_qdict(opts, options, &local_err);
     if (error_is_set(&local_err)) {
-        qerror_report_err(local_err);
-        error_free(local_err);
+        error_propagate(errp, local_err);
         ret = -EINVAL;
         goto fail;
     }
@@ -608,8 +634,8 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
     qemu_opts_del(opts);
 
     if (s->use_lazy_refcounts && s->qcow_version < 3) {
-        qerror_report(ERROR_CLASS_GENERIC_ERROR, "Lazy refcounts require "
-            "a qcow2 image with at least qemu 1.1 compatibility level");
+        error_setg(errp, "Lazy refcounts require a qcow2 image with at least "
+                   "qemu 1.1 compatibility level");
         ret = -EINVAL;
         goto fail;
     }
@@ -1059,7 +1085,7 @@ static void qcow2_invalidate_cache(BlockDriverState *bs)
               qbool_from_int(s->use_lazy_refcounts));
 
     memset(s, 0, sizeof(BDRVQcowState));
-    qcow2_open(bs, options, flags);
+    qcow2_open(bs, options, flags, NULL);
 
     QDECREF(options);
 
@@ -1143,7 +1169,7 @@ int qcow2_update_header(BlockDriverState *bs)
         .incompatible_features  = cpu_to_be64(s->incompatible_features),
         .compatible_features    = cpu_to_be64(s->compatible_features),
         .autoclear_features     = cpu_to_be64(s->autoclear_features),
-        .refcount_order         = cpu_to_be32(3 + REFCOUNT_SHIFT),
+        .refcount_order         = cpu_to_be32(s->refcount_order),
         .header_length          = cpu_to_be32(header_length),
     };
 
@@ -1332,7 +1358,8 @@ static int preallocate(BlockDriverState *bs)
 static int qcow2_create2(const char *filename, int64_t total_size,
                          const char *backing_file, const char *backing_format,
                          int flags, size_t cluster_size, int prealloc,
-                         QEMUOptionParameter *options, int version)
+                         QEMUOptionParameter *options, int version,
+                         Error **errp)
 {
     /* Calculate cluster_bits */
     int cluster_bits;
@@ -1340,9 +1367,8 @@ static int qcow2_create2(const char *filename, int64_t total_size,
     if (cluster_bits < MIN_CLUSTER_BITS || cluster_bits > MAX_CLUSTER_BITS ||
         (1 << cluster_bits) != cluster_size)
     {
-        error_report(
-            "Cluster size must be a power of two between %d and %dk",
-            1 << MIN_CLUSTER_BITS, 1 << (MAX_CLUSTER_BITS - 10));
+        error_setg(errp, "Cluster size must be a power of two between %d and "
+                   "%dk", 1 << MIN_CLUSTER_BITS, 1 << (MAX_CLUSTER_BITS - 10));
         return -EINVAL;
     }
 
@@ -1361,15 +1387,18 @@ static int qcow2_create2(const char *filename, int64_t total_size,
     BlockDriverState* bs;
     QCowHeader header;
     uint8_t* refcount_table;
+    Error *local_err = NULL;
     int ret;
 
-    ret = bdrv_create_file(filename, options);
+    ret = bdrv_create_file(filename, options, &local_err);
     if (ret < 0) {
+        error_propagate(errp, local_err);
         return ret;
     }
 
-    ret = bdrv_file_open(&bs, filename, NULL, BDRV_O_RDWR);
+    ret = bdrv_file_open(&bs, filename, NULL, BDRV_O_RDWR, &local_err);
     if (ret < 0) {
+        error_propagate(errp, local_err);
         return ret;
     }
 
@@ -1399,6 +1428,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
 
     ret = bdrv_pwrite(bs, 0, &header, sizeof(header));
     if (ret < 0) {
+        error_setg_errno(errp, -ret, "Could not write qcow2 header");
         goto out;
     }
 
@@ -1408,6 +1438,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
     g_free(refcount_table);
 
     if (ret < 0) {
+        error_setg_errno(errp, -ret, "Could not write refcount table");
         goto out;
     }
 
@@ -1421,13 +1452,16 @@ static int qcow2_create2(const char *filename, int64_t total_size,
     BlockDriver* drv = bdrv_find_format("qcow2");
     assert(drv != NULL);
     ret = bdrv_open(bs, filename, NULL,
-        BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, drv);
+        BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, drv, &local_err);
     if (ret < 0) {
+        error_propagate(errp, local_err);
         goto out;
     }
 
     ret = qcow2_alloc_clusters(bs, 2 * cluster_size);
     if (ret < 0) {
+        error_setg_errno(errp, -ret, "Could not allocate clusters for qcow2 "
+                         "header and refcount table");
         goto out;
 
     } else if (ret != 0) {
@@ -1438,6 +1472,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
     /* Okay, now that we have a valid image, let's give it the right size */
     ret = bdrv_truncate(bs, total_size * BDRV_SECTOR_SIZE);
     if (ret < 0) {
+        error_setg_errno(errp, -ret, "Could not resize image");
         goto out;
     }
 
@@ -1445,6 +1480,8 @@ static int qcow2_create2(const char *filename, int64_t total_size,
     if (backing_file) {
         ret = bdrv_change_backing_file(bs, backing_file, backing_format);
         if (ret < 0) {
+            error_setg_errno(errp, -ret, "Could not assign backing file '%s' "
+                             "with format '%s'", backing_file, backing_format);
             goto out;
         }
     }
@@ -1456,6 +1493,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
         ret = preallocate(bs);
         qemu_co_mutex_unlock(&s->lock);
         if (ret < 0) {
+            error_setg_errno(errp, -ret, "Could not preallocate metadata");
             goto out;
         }
     }
@@ -1466,7 +1504,8 @@ out:
     return ret;
 }
 
-static int qcow2_create(const char *filename, QEMUOptionParameter *options)
+static int qcow2_create(const char *filename, QEMUOptionParameter *options,
+                        Error **errp)
 {
     const char *backing_file = NULL;
     const char *backing_fmt = NULL;
@@ -1475,6 +1514,8 @@ static int qcow2_create(const char *filename, QEMUOptionParameter *options)
     size_t cluster_size = DEFAULT_CLUSTER_SIZE;
     int prealloc = 0;
     int version = 3;
+    Error *local_err = NULL;
+    int ret;
 
     /* Read out options */
     while (options && options->name) {
@@ -1496,8 +1537,8 @@ static int qcow2_create(const char *filename, QEMUOptionParameter *options)
             } else if (!strcmp(options->value.s, "metadata")) {
                 prealloc = 1;
             } else {
-                fprintf(stderr, "Invalid preallocation mode: '%s'\n",
-                    options->value.s);
+                error_setg(errp, "Invalid preallocation mode: '%s'",
+                           options->value.s);
                 return -EINVAL;
             }
         } else if (!strcmp(options->name, BLOCK_OPT_COMPAT_LEVEL)) {
@@ -1508,8 +1549,8 @@ static int qcow2_create(const char *filename, QEMUOptionParameter *options)
             } else if (!strcmp(options->value.s, "1.1")) {
                 version = 3;
             } else {
-                fprintf(stderr, "Invalid compatibility level: '%s'\n",
-                    options->value.s);
+                error_setg(errp, "Invalid compatibility level: '%s'",
+                           options->value.s);
                 return -EINVAL;
             }
         } else if (!strcmp(options->name, BLOCK_OPT_LAZY_REFCOUNTS)) {
@@ -1519,19 +1560,23 @@ static int qcow2_create(const char *filename, QEMUOptionParameter *options)
     }
 
     if (backing_file && prealloc) {
-        fprintf(stderr, "Backing file and preallocation cannot be used at "
-            "the same time\n");
+        error_setg(errp, "Backing file and preallocation cannot be used at "
+                   "the same time");
         return -EINVAL;
     }
 
     if (version < 3 && (flags & BLOCK_FLAG_LAZY_REFCOUNTS)) {
-        fprintf(stderr, "Lazy refcounts only supported with compatibility "
-                "level 1.1 and above (use compat=1.1 or greater)\n");
+        error_setg(errp, "Lazy refcounts only supported with compatibility "
+                   "level 1.1 and above (use compat=1.1 or greater)");
         return -EINVAL;
     }
 
-    return qcow2_create2(filename, sectors, backing_file, backing_fmt, flags,
-                         cluster_size, prealloc, options, version);
+    ret = qcow2_create2(filename, sectors, backing_file, backing_fmt, flags,
+                        cluster_size, prealloc, options, version, &local_err);
+    if (error_is_set(&local_err)) {
+        error_propagate(errp, local_err);
+    }
+    return ret;
 }
 
 static int qcow2_make_empty(BlockDriverState *bs)
@@ -1582,7 +1627,7 @@ static coroutine_fn int qcow2_co_discard(BlockDriverState *bs,
 
     qemu_co_mutex_lock(&s->lock);
     ret = qcow2_discard_clusters(bs, sector_num << BDRV_SECTOR_BITS,
-        nb_sectors);
+        nb_sectors, QCOW2_DISCARD_REQUEST);
     qemu_co_mutex_unlock(&s->lock);
     return ret;
 }
@@ -1757,11 +1802,6 @@ static coroutine_fn int qcow2_co_flush_to_os(BlockDriverState *bs)
     return 0;
 }
 
-static int64_t qcow2_vm_state_offset(BDRVQcowState *s)
-{
-       return (int64_t)s->l1_vm_state_index << (s->cluster_bits + s->l2_bits);
-}
-
 static int qcow2_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
 {
     BDRVQcowState *s = bs->opaque;
@@ -1824,6 +1864,199 @@ static int qcow2_load_vmstate(BlockDriverState *bs, uint8_t *buf,
     return ret;
 }
 
+/*
+ * Downgrades an image's version. To achieve this, any incompatible features
+ * have to be removed.
+ */
+static int qcow2_downgrade(BlockDriverState *bs, int target_version)
+{
+    BDRVQcowState *s = bs->opaque;
+    int current_version = s->qcow_version;
+    int ret;
+
+    if (target_version == current_version) {
+        return 0;
+    } else if (target_version > current_version) {
+        return -EINVAL;
+    } else if (target_version != 2) {
+        return -EINVAL;
+    }
+
+    if (s->refcount_order != 4) {
+        /* we would have to convert the image to a refcount_order == 4 image
+         * here; however, since qemu (at the time of writing this) does not
+         * support anything different than 4 anyway, there is no point in doing
+         * so right now; however, we should error out (if qemu supports this in
+         * the future and this code has not been adapted) */
+        error_report("qcow2_downgrade: Image refcount orders other than 4 are"
+                     "currently not supported.");
+        return -ENOTSUP;
+    }
+
+    /* clear incompatible features */
+    if (s->incompatible_features & QCOW2_INCOMPAT_DIRTY) {
+        ret = qcow2_mark_clean(bs);
+        if (ret < 0) {
+            return ret;
+        }
+    }
+
+    /* with QCOW2_INCOMPAT_CORRUPT, it is pretty much impossible to get here in
+     * the first place; if that happens nonetheless, returning -ENOTSUP is the
+     * best thing to do anyway */
+
+    if (s->incompatible_features) {
+        return -ENOTSUP;
+    }
+
+    /* since we can ignore compatible features, we can set them to 0 as well */
+    s->compatible_features = 0;
+    /* if lazy refcounts have been used, they have already been fixed through
+     * clearing the dirty flag */
+
+    /* clearing autoclear features is trivial */
+    s->autoclear_features = 0;
+
+    ret = qcow2_expand_zero_clusters(bs);
+    if (ret < 0) {
+        return ret;
+    }
+
+    s->qcow_version = target_version;
+    ret = qcow2_update_header(bs);
+    if (ret < 0) {
+        s->qcow_version = current_version;
+        return ret;
+    }
+    return 0;
+}
+
+static int qcow2_amend_options(BlockDriverState *bs,
+                               QEMUOptionParameter *options)
+{
+    BDRVQcowState *s = bs->opaque;
+    int old_version = s->qcow_version, new_version = old_version;
+    uint64_t new_size = 0;
+    const char *backing_file = NULL, *backing_format = NULL;
+    bool lazy_refcounts = s->use_lazy_refcounts;
+    int ret;
+    int i;
+
+    for (i = 0; options[i].name; i++)
+    {
+        if (!options[i].assigned) {
+            /* only change explicitly defined options */
+            continue;
+        }
+
+        if (!strcmp(options[i].name, "compat")) {
+            if (!options[i].value.s) {
+                /* preserve default */
+            } else if (!strcmp(options[i].value.s, "0.10")) {
+                new_version = 2;
+            } else if (!strcmp(options[i].value.s, "1.1")) {
+                new_version = 3;
+            } else {
+                fprintf(stderr, "Unknown compatibility level %s.\n",
+                        options[i].value.s);
+                return -EINVAL;
+            }
+        } else if (!strcmp(options[i].name, "preallocation")) {
+            fprintf(stderr, "Cannot change preallocation mode.\n");
+            return -ENOTSUP;
+        } else if (!strcmp(options[i].name, "size")) {
+            new_size = options[i].value.n;
+        } else if (!strcmp(options[i].name, "backing_file")) {
+            backing_file = options[i].value.s;
+        } else if (!strcmp(options[i].name, "backing_fmt")) {
+            backing_format = options[i].value.s;
+        } else if (!strcmp(options[i].name, "encryption")) {
+            if ((options[i].value.n != !!s->crypt_method)) {
+                fprintf(stderr, "Changing the encryption flag is not "
+                        "supported.\n");
+                return -ENOTSUP;
+            }
+        } else if (!strcmp(options[i].name, "cluster_size")) {
+            if (options[i].value.n != s->cluster_size) {
+                fprintf(stderr, "Changing the cluster size is not "
+                        "supported.\n");
+                return -ENOTSUP;
+            }
+        } else if (!strcmp(options[i].name, "lazy_refcounts")) {
+            lazy_refcounts = options[i].value.n;
+        } else {
+            /* if this assertion fails, this probably means a new option was
+             * added without having it covered here */
+            assert(false);
+        }
+    }
+
+    if (new_version != old_version) {
+        if (new_version > old_version) {
+            /* Upgrade */
+            s->qcow_version = new_version;
+            ret = qcow2_update_header(bs);
+            if (ret < 0) {
+                s->qcow_version = old_version;
+                return ret;
+            }
+        } else {
+            ret = qcow2_downgrade(bs, new_version);
+            if (ret < 0) {
+                return ret;
+            }
+        }
+    }
+
+    if (backing_file || backing_format) {
+        ret = qcow2_change_backing_file(bs, backing_file ?: bs->backing_file,
+                                        backing_format ?: bs->backing_format);
+        if (ret < 0) {
+            return ret;
+        }
+    }
+
+    if (s->use_lazy_refcounts != lazy_refcounts) {
+        if (lazy_refcounts) {
+            if (s->qcow_version < 3) {
+                fprintf(stderr, "Lazy refcounts only supported with compatibility "
+                        "level 1.1 and above (use compat=1.1 or greater)\n");
+                return -EINVAL;
+            }
+            s->compatible_features |= QCOW2_COMPAT_LAZY_REFCOUNTS;
+            ret = qcow2_update_header(bs);
+            if (ret < 0) {
+                s->compatible_features &= ~QCOW2_COMPAT_LAZY_REFCOUNTS;
+                return ret;
+            }
+            s->use_lazy_refcounts = true;
+        } else {
+            /* make image clean first */
+            ret = qcow2_mark_clean(bs);
+            if (ret < 0) {
+                return ret;
+            }
+            /* now disallow lazy refcounts */
+            s->compatible_features &= ~QCOW2_COMPAT_LAZY_REFCOUNTS;
+            ret = qcow2_update_header(bs);
+            if (ret < 0) {
+                s->compatible_features |= QCOW2_COMPAT_LAZY_REFCOUNTS;
+                return ret;
+            }
+            s->use_lazy_refcounts = false;
+        }
+    }
+
+    if (new_size) {
+        ret = bdrv_truncate(bs, new_size);
+        if (ret < 0) {
+            return ret;
+        }
+    }
+
+    return 0;
+}
+
 static QEMUOptionParameter qcow2_create_options[] = {
     {
         .name = BLOCK_OPT_SIZE,
@@ -1907,6 +2140,7 @@ static BlockDriver bdrv_qcow2 = {
 
     .create_options = qcow2_create_options,
     .bdrv_check = qcow2_check,
+    .bdrv_amend_options = qcow2_amend_options,
 };
 
 static void bdrv_qcow2_init(void)
index 1000239e4c5f6592a5a83d6386d778e3bdeccc86..c90e5d6c6e6cca0f12afb523b98577d958923373 100644 (file)
@@ -199,6 +199,7 @@ typedef struct BDRVQcowState {
     int flags;
     int qcow_version;
     bool use_lazy_refcounts;
+    int refcount_order;
 
     bool discard_passthrough[QCOW2_DISCARD_MAX];
 
@@ -361,6 +362,11 @@ static inline int64_t align_offset(int64_t offset, int n)
     return offset;
 }
 
+static inline int64_t qcow2_vm_state_offset(BDRVQcowState *s)
+{
+    return (int64_t)s->l1_vm_state_index << (s->cluster_bits + s->l2_bits);
+}
+
 static inline int qcow2_get_cluster_type(uint64_t l2_entry)
 {
     if (l2_entry & QCOW_OFLAG_COMPRESSED) {
@@ -406,6 +412,9 @@ int qcow2_update_header(BlockDriverState *bs);
 int qcow2_refcount_init(BlockDriverState *bs);
 void qcow2_refcount_close(BlockDriverState *bs);
 
+int qcow2_update_cluster_refcount(BlockDriverState *bs, int64_t cluster_index,
+                                  int addend, enum qcow2_discard_type type);
+
 int64_t qcow2_alloc_clusters(BlockDriverState *bs, int64_t size);
 int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset,
     int nb_clusters);
@@ -450,13 +459,18 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
 
 int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m);
 int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset,
-    int nb_sectors);
+    int nb_sectors, enum qcow2_discard_type type);
 int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors);
 
+int qcow2_expand_zero_clusters(BlockDriverState *bs);
+
 /* qcow2-snapshot.c functions */
 int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info);
 int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id);
-int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id);
+int qcow2_snapshot_delete(BlockDriverState *bs,
+                          const char *snapshot_id,
+                          const char *name,
+                          Error **errp);
 int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab);
 int qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_name);
 
@@ -473,6 +487,8 @@ int qcow2_cache_set_dependency(BlockDriverState *bs, Qcow2Cache *c,
     Qcow2Cache *dependency);
 void qcow2_cache_depends_on_flush(Qcow2Cache *c);
 
+int qcow2_cache_empty(BlockDriverState *bs, Qcow2Cache *c);
+
 int qcow2_cache_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset,
     void **table);
 int qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset,
index 49b3a37ed58df158a14270d2b677cadc6675ac0b..6c0cba04f3f43826ba463a10864335b0abd89d3b 100644 (file)
@@ -373,7 +373,8 @@ static void bdrv_qed_rebind(BlockDriverState *bs)
     s->bs = bs;
 }
 
-static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags)
+static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags,
+                         Error **errp)
 {
     BDRVQEDState *s = bs->opaque;
     QEDHeader le_header;
@@ -550,16 +551,22 @@ static int qed_create(const char *filename, uint32_t cluster_size,
     QEDHeader le_header;
     uint8_t *l1_table = NULL;
     size_t l1_size = header.cluster_size * header.table_size;
+    Error *local_err = NULL;
     int ret = 0;
     BlockDriverState *bs = NULL;
 
-    ret = bdrv_create_file(filename, NULL);
+    ret = bdrv_create_file(filename, NULL, &local_err);
     if (ret < 0) {
+        qerror_report_err(local_err);
+        error_free(local_err);
         return ret;
     }
 
-    ret = bdrv_file_open(&bs, filename, NULL, BDRV_O_RDWR | BDRV_O_CACHE_WB);
+    ret = bdrv_file_open(&bs, filename, NULL, BDRV_O_RDWR | BDRV_O_CACHE_WB,
+                         &local_err);
     if (ret < 0) {
+        qerror_report_err(local_err);
+        error_free(local_err);
         return ret;
     }
 
@@ -603,7 +610,8 @@ out:
     return ret;
 }
 
-static int bdrv_qed_create(const char *filename, QEMUOptionParameter *options)
+static int bdrv_qed_create(const char *filename, QEMUOptionParameter *options,
+                           Error **errp)
 {
     uint64_t image_size = 0;
     uint32_t cluster_size = QED_DEFAULT_CLUSTER_SIZE;
@@ -1547,7 +1555,7 @@ static void bdrv_qed_invalidate_cache(BlockDriverState *bs)
 
     bdrv_qed_close(bs);
     memset(s, 0, sizeof(BDRVQEDState));
-    bdrv_qed_open(bs, NULL, bs->open_flags);
+    bdrv_qed_open(bs, NULL, bs->open_flags, NULL);
 }
 
 static int bdrv_qed_check(BlockDriverState *bs, BdrvCheckResult *result,
index 1b41ea335695ff5f89d57f2f15bbfc602c3602de..3ee5b625090f38855a88c089bbad5e33678b70d1 100644 (file)
@@ -335,7 +335,8 @@ fail:
     return ret;
 }
 
-static int raw_open(BlockDriverState *bs, QDict *options, int flags)
+static int raw_open(BlockDriverState *bs, QDict *options, int flags,
+                    Error **errp)
 {
     BDRVRawState *s = bs->opaque;
 
@@ -1040,7 +1041,8 @@ static int64_t raw_get_allocated_file_size(BlockDriverState *bs)
     return (int64_t)st.st_blocks * 512;
 }
 
-static int raw_create(const char *filename, QEMUOptionParameter *options)
+static int raw_create(const char *filename, QEMUOptionParameter *options,
+                      Error **errp)
 {
     int fd;
     int result = 0;
@@ -1331,7 +1333,8 @@ static int check_hdev_writable(BDRVRawState *s)
     return 0;
 }
 
-static int hdev_open(BlockDriverState *bs, QDict *options, int flags)
+static int hdev_open(BlockDriverState *bs, QDict *options, int flags,
+                     Error **errp)
 {
     BDRVRawState *s = bs->opaque;
     int ret;
@@ -1504,7 +1507,8 @@ static coroutine_fn BlockDriverAIOCB *hdev_aio_discard(BlockDriverState *bs,
                        cb, opaque, QEMU_AIO_DISCARD|QEMU_AIO_BLKDEV);
 }
 
-static int hdev_create(const char *filename, QEMUOptionParameter *options)
+static int hdev_create(const char *filename, QEMUOptionParameter *options,
+                       Error **errp)
 {
     int fd;
     int ret = 0;
@@ -1565,7 +1569,8 @@ static BlockDriver bdrv_host_device = {
 };
 
 #ifdef __linux__
-static int floppy_open(BlockDriverState *bs, QDict *options, int flags)
+static int floppy_open(BlockDriverState *bs, QDict *options, int flags,
+                       Error **errp)
 {
     BDRVRawState *s = bs->opaque;
     int ret;
@@ -1686,7 +1691,8 @@ static BlockDriver bdrv_host_floppy = {
     .bdrv_eject         = floppy_eject,
 };
 
-static int cdrom_open(BlockDriverState *bs, QDict *options, int flags)
+static int cdrom_open(BlockDriverState *bs, QDict *options, int flags,
+                      Error **errp)
 {
     BDRVRawState *s = bs->opaque;
 
index d2d2d9f4d41eda66a406548aca39e9c211d0a8aa..1e7651be61ed6820d4e2bcd0fc9b7a7a5753cd5a 100644 (file)
@@ -85,6 +85,7 @@ static size_t handle_aiocb_rw(RawWin32AIOData *aiocb)
             ret_count = 0;
         }
         if (ret_count != len) {
+            offset += ret_count;
             break;
         }
         offset += len;
@@ -234,7 +235,8 @@ static QemuOptsList raw_runtime_opts = {
     },
 };
 
-static int raw_open(BlockDriverState *bs, QDict *options, int flags)
+static int raw_open(BlockDriverState *bs, QDict *options, int flags,
+                    Error **errp)
 {
     BDRVRawState *s = bs->opaque;
     int access_flags;
@@ -420,7 +422,8 @@ static int64_t raw_get_allocated_file_size(BlockDriverState *bs)
     return st.st_size;
 }
 
-static int raw_create(const char *filename, QEMUOptionParameter *options)
+static int raw_create(const char *filename, QEMUOptionParameter *options,
+                      Error **errp)
 {
     int fd;
     int64_t total_size = 0;
@@ -531,7 +534,8 @@ static int hdev_probe_device(const char *filename)
     return 0;
 }
 
-static int hdev_open(BlockDriverState *bs, QDict *options, int flags)
+static int hdev_open(BlockDriverState *bs, QDict *options, int flags,
+                     Error **errp)
 {
     BDRVRawState *s = bs->opaque;
     int access_flags, create_flags;
index a9060caec403d4cef68c7b9dc37a32802f042c21..d4ace6020b956b6c48b9ff145b7ec06d81796d5b 100644 (file)
@@ -130,12 +130,22 @@ static int raw_has_zero_init(BlockDriverState *bs)
     return bdrv_has_zero_init(bs->file);
 }
 
-static int raw_create(const char *filename, QEMUOptionParameter *options)
+static int raw_create(const char *filename, QEMUOptionParameter *options,
+                      Error **errp)
 {
-    return bdrv_create_file(filename, options);
+    Error *local_err = NULL;
+    int ret;
+
+    ret = bdrv_create_file(filename, options, &local_err);
+    if (error_is_set(&local_err)) {
+        qerror_report_err(local_err);
+        error_free(local_err);
+    }
+    return ret;
 }
 
-static int raw_open(BlockDriverState *bs, QDict *options, int flags)
+static int raw_open(BlockDriverState *bs, QDict *options, int flags,
+                    Error **errp)
 {
     bs->sg = bs->file->sg;
     return 0;
index e798e19f814180998ffb0a2490d8b733d256f326..11086c35c480d6b7ed39892b4b5346f5dc7ecd38 100644 (file)
@@ -287,7 +287,8 @@ static int qemu_rbd_set_conf(rados_t cluster, const char *conf)
     return ret;
 }
 
-static int qemu_rbd_create(const char *filename, QEMUOptionParameter *options)
+static int qemu_rbd_create(const char *filename, QEMUOptionParameter *options,
+                           Error **errp)
 {
     int64_t bytes = 0;
     int64_t objsize;
@@ -446,7 +447,8 @@ static QemuOptsList runtime_opts = {
     },
 };
 
-static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags)
+static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
+                         Error **errp)
 {
     BDRVRBDState *s = bs->opaque;
     char pool[RBD_MAX_POOL_NAME_SIZE];
@@ -891,12 +893,31 @@ static int qemu_rbd_snap_create(BlockDriverState *bs,
 }
 
 static int qemu_rbd_snap_remove(BlockDriverState *bs,
-                                const char *snapshot_name)
+                                const char *snapshot_id,
+                                const char *snapshot_name,
+                                Error **errp)
 {
     BDRVRBDState *s = bs->opaque;
     int r;
 
+    if (!snapshot_name) {
+        error_setg(errp, "rbd need a valid snapshot name");
+        return -EINVAL;
+    }
+
+    /* If snapshot_id is specified, it must be equal to name, see
+       qemu_rbd_snap_list() */
+    if (snapshot_id && strcmp(snapshot_id, snapshot_name)) {
+        error_setg(errp,
+                   "rbd do not support snapshot id, it should be NULL or "
+                   "equal to snapshot name");
+        return -EINVAL;
+    }
+
     r = rbd_snap_remove(s->image, snapshot_name);
+    if (r < 0) {
+        error_setg_errno(errp, -r, "Failed to remove the snapshot");
+    }
     return r;
 }
 
index f9988d35ba3fd6a954182acc4098adb6c9a38008..38fb6296501c0f4f69a6e189a924379464aca05d 100644 (file)
@@ -1242,7 +1242,8 @@ static QemuOptsList runtime_opts = {
     },
 };
 
-static int sd_open(BlockDriverState *bs, QDict *options, int flags)
+static int sd_open(BlockDriverState *bs, QDict *options, int flags,
+                   Error **errp)
 {
     int ret, fd;
     uint32_t vid = 0;
@@ -1400,10 +1401,13 @@ static int sd_prealloc(const char *filename)
     uint32_t idx, max_idx;
     int64_t vdi_size;
     void *buf = g_malloc0(SD_DATA_OBJ_SIZE);
+    Error *local_err = NULL;
     int ret;
 
-    ret = bdrv_file_open(&bs, filename, NULL, BDRV_O_RDWR);
+    ret = bdrv_file_open(&bs, filename, NULL, BDRV_O_RDWR, &local_err);
     if (ret < 0) {
+        qerror_report_err(local_err);
+        error_free(local_err);
         goto out;
     }
 
@@ -1437,7 +1441,8 @@ out:
     return ret;
 }
 
-static int sd_create(const char *filename, QEMUOptionParameter *options)
+static int sd_create(const char *filename, QEMUOptionParameter *options,
+                     Error **errp)
 {
     int ret = 0;
     uint32_t vid = 0, base_vid = 0;
@@ -1447,6 +1452,7 @@ static int sd_create(const char *filename, QEMUOptionParameter *options)
     char vdi[SD_MAX_VDI_LEN], tag[SD_MAX_VDI_TAG_LEN];
     uint32_t snapid;
     bool prealloc = false;
+    Error *local_err = NULL;
 
     s = g_malloc0(sizeof(BDRVSheepdogState));
 
@@ -1500,8 +1506,10 @@ static int sd_create(const char *filename, QEMUOptionParameter *options)
             goto out;
         }
 
-        ret = bdrv_file_open(&bs, backing_file, NULL, 0);
+        ret = bdrv_file_open(&bs, backing_file, NULL, 0, &local_err);
         if (ret < 0) {
+            qerror_report_err(local_err);
+            error_free(local_err);
             goto out;
         }
 
@@ -2072,7 +2080,10 @@ out:
     return ret;
 }
 
-static int sd_snapshot_delete(BlockDriverState *bs, const char *snapshot_id)
+static int sd_snapshot_delete(BlockDriverState *bs,
+                              const char *snapshot_id,
+                              const char *name,
+                              Error **errp)
 {
     /* FIXME: Delete specified snapshot id.  */
     return 0;
index 8f61cc0745ecffc637cd111a597e61d4ed7f6b32..a05c0c0be03437eddbc990be68eabf909a0c1075 100644 (file)
@@ -48,6 +48,79 @@ int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info,
     return ret;
 }
 
+/**
+ * Look up an internal snapshot by @id and @name.
+ * @bs: block device to search
+ * @id: unique snapshot ID, or NULL
+ * @name: snapshot name, or NULL
+ * @sn_info: location to store information on the snapshot found
+ * @errp: location to store error, will be set only for exception
+ *
+ * This function will traverse snapshot list in @bs to search the matching
+ * one, @id and @name are the matching condition:
+ * If both @id and @name are specified, find the first one with id @id and
+ * name @name.
+ * If only @id is specified, find the first one with id @id.
+ * If only @name is specified, find the first one with name @name.
+ * if none is specified, abort().
+ *
+ * Returns: true when a snapshot is found and @sn_info will be filled, false
+ * when error or not found. If all operation succeed but no matching one is
+ * found, @errp will NOT be set.
+ */
+bool bdrv_snapshot_find_by_id_and_name(BlockDriverState *bs,
+                                       const char *id,
+                                       const char *name,
+                                       QEMUSnapshotInfo *sn_info,
+                                       Error **errp)
+{
+    QEMUSnapshotInfo *sn_tab, *sn;
+    int nb_sns, i;
+    bool ret = false;
+
+    assert(id || name);
+
+    nb_sns = bdrv_snapshot_list(bs, &sn_tab);
+    if (nb_sns < 0) {
+        error_setg_errno(errp, -nb_sns, "Failed to get a snapshot list");
+        return false;
+    } else if (nb_sns == 0) {
+        return false;
+    }
+
+    if (id && name) {
+        for (i = 0; i < nb_sns; i++) {
+            sn = &sn_tab[i];
+            if (!strcmp(sn->id_str, id) && !strcmp(sn->name, name)) {
+                *sn_info = *sn;
+                ret = true;
+                break;
+            }
+        }
+    } else if (id) {
+        for (i = 0; i < nb_sns; i++) {
+            sn = &sn_tab[i];
+            if (!strcmp(sn->id_str, id)) {
+                *sn_info = *sn;
+                ret = true;
+                break;
+            }
+        }
+    } else if (name) {
+        for (i = 0; i < nb_sns; i++) {
+            sn = &sn_tab[i];
+            if (!strcmp(sn->name, name)) {
+                *sn_info = *sn;
+                ret = true;
+                break;
+            }
+        }
+    }
+
+    g_free(sn_tab);
+    return ret;
+}
+
 int bdrv_can_snapshot(BlockDriverState *bs)
 {
     BlockDriver *drv = bs->drv;
@@ -97,7 +170,7 @@ int bdrv_snapshot_goto(BlockDriverState *bs,
     if (bs->file) {
         drv->bdrv_close(bs);
         ret = bdrv_snapshot_goto(bs->file, snapshot_id);
-        open_ret = drv->bdrv_open(bs, NULL, bs->open_flags);
+        open_ret = drv->bdrv_open(bs, NULL, bs->open_flags, NULL);
         if (open_ret < 0) {
             bdrv_unref(bs->file);
             bs->drv = NULL;
@@ -109,21 +182,73 @@ int bdrv_snapshot_goto(BlockDriverState *bs,
     return -ENOTSUP;
 }
 
-int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id)
+/**
+ * Delete an internal snapshot by @snapshot_id and @name.
+ * @bs: block device used in the operation
+ * @snapshot_id: unique snapshot ID, or NULL
+ * @name: snapshot name, or NULL
+ * @errp: location to store error
+ *
+ * If both @snapshot_id and @name are specified, delete the first one with
+ * id @snapshot_id and name @name.
+ * If only @snapshot_id is specified, delete the first one with id
+ * @snapshot_id.
+ * If only @name is specified, delete the first one with name @name.
+ * if none is specified, return -ENINVAL.
+ *
+ * Returns: 0 on success, -errno on failure. If @bs is not inserted, return
+ * -ENOMEDIUM. If @snapshot_id and @name are both NULL, return -EINVAL. If @bs
+ * does not support internal snapshot deletion, return -ENOTSUP. If @bs does
+ * not support parameter @snapshot_id or @name, or one of them is not correctly
+ * specified, return -EINVAL. If @bs can't find one matching @id and @name,
+ * return -ENOENT. If @errp != NULL, it will always be filled with error
+ * message on failure.
+ */
+int bdrv_snapshot_delete(BlockDriverState *bs,
+                         const char *snapshot_id,
+                         const char *name,
+                         Error **errp)
 {
     BlockDriver *drv = bs->drv;
     if (!drv) {
+        error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, bdrv_get_device_name(bs));
         return -ENOMEDIUM;
     }
+    if (!snapshot_id && !name) {
+        error_setg(errp, "snapshot_id and name are both NULL");
+        return -EINVAL;
+    }
     if (drv->bdrv_snapshot_delete) {
-        return drv->bdrv_snapshot_delete(bs, snapshot_id);
+        return drv->bdrv_snapshot_delete(bs, snapshot_id, name, errp);
     }
     if (bs->file) {
-        return bdrv_snapshot_delete(bs->file, snapshot_id);
+        return bdrv_snapshot_delete(bs->file, snapshot_id, name, errp);
     }
+    error_set(errp, QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED,
+              drv->format_name, bdrv_get_device_name(bs),
+              "internal snapshot deletion");
     return -ENOTSUP;
 }
 
+void bdrv_snapshot_delete_by_id_or_name(BlockDriverState *bs,
+                                        const char *id_or_name,
+                                        Error **errp)
+{
+    int ret;
+    Error *local_err = NULL;
+
+    ret = bdrv_snapshot_delete(bs, id_or_name, NULL, &local_err);
+    if (ret == -ENOENT || ret == -EINVAL) {
+        error_free(local_err);
+        local_err = NULL;
+        ret = bdrv_snapshot_delete(bs, NULL, id_or_name, &local_err);
+    }
+
+    if (ret < 0) {
+        error_propagate(errp, local_err);
+    }
+}
+
 int bdrv_snapshot_list(BlockDriverState *bs,
                        QEMUSnapshotInfo **psn_info)
 {
index 27691b4ad5f0e7c6e94d118f7870452a8ce2cc3f..aa63c9d20ee3eedd6714c1bcdba7285c6a3fc366 100644 (file)
@@ -608,7 +608,8 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
     return ret;
 }
 
-static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags)
+static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags,
+                         Error **errp)
 {
     BDRVSSHState *s = bs->opaque;
     int ret;
@@ -650,7 +651,8 @@ static QEMUOptionParameter ssh_create_options[] = {
     { NULL }
 };
 
-static int ssh_create(const char *filename, QEMUOptionParameter *options)
+static int ssh_create(const char *filename, QEMUOptionParameter *options,
+                      Error **errp)
 {
     int r, ret;
     Error *local_err = NULL;
index 1bf7dc575a9c6dce3967b1dcfdef27a77440affc..dcbc27c9cbd40dec6f082849a1a0e9b80066937e 100644 (file)
@@ -364,7 +364,8 @@ static int vdi_probe(const uint8_t *buf, int buf_size, const char *filename)
     return result;
 }
 
-static int vdi_open(BlockDriverState *bs, QDict *options, int flags)
+static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
+                    Error **errp)
 {
     BDRVVdiState *s = bs->opaque;
     VdiHeader header;
@@ -644,7 +645,8 @@ static int vdi_co_write(BlockDriverState *bs,
     return ret;
 }
 
-static int vdi_create(const char *filename, QEMUOptionParameter *options)
+static int vdi_create(const char *filename, QEMUOptionParameter *options,
+                      Error **errp)
 {
     int fd;
     int result = 0;
index e9704b1fdc1c119ea52280b3080bd6c729c63b9e..b8aa49ce4eb5664276fd8300f1a3db23fcc68d10 100644 (file)
@@ -715,7 +715,8 @@ exit:
 }
 
 
-static int vhdx_open(BlockDriverState *bs, QDict *options, int flags)
+static int vhdx_open(BlockDriverState *bs, QDict *options, int flags,
+                     Error **errp)
 {
     BDRVVHDXState *s = bs->opaque;
     int ret = 0;
index fb5b5297ce9e45cbd2197ffe8a5bda62d695dc9d..96ef1b534e53151e8064eb92fcb3a788e5c2909d 100644 (file)
@@ -697,6 +697,7 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
     int64_t flat_offset;
     char extent_path[PATH_MAX];
     BlockDriverState *extent_file;
+    Error *local_err = NULL;
 
     while (*p) {
         /* parse extent line:
@@ -726,8 +727,11 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
 
         path_combine(extent_path, sizeof(extent_path),
                 desc_file_path, fname);
-        ret = bdrv_file_open(&extent_file, extent_path, NULL, bs->open_flags);
+        ret = bdrv_file_open(&extent_file, extent_path, NULL, bs->open_flags,
+                             &local_err);
         if (ret) {
+            qerror_report_err(local_err);
+            error_free(local_err);
             return ret;
         }
 
@@ -806,7 +810,8 @@ exit:
     return ret;
 }
 
-static int vmdk_open(BlockDriverState *bs, QDict *options, int flags)
+static int vmdk_open(BlockDriverState *bs, QDict *options, int flags,
+                     Error **errp)
 {
     int ret;
     BDRVVmdkState *s = bs->opaque;
@@ -1551,7 +1556,8 @@ static int filename_decompose(const char *filename, char *path, char *prefix,
     return VMDK_OK;
 }
 
-static int vmdk_create(const char *filename, QEMUOptionParameter *options)
+static int vmdk_create(const char *filename, QEMUOptionParameter *options,
+                       Error **errp)
 {
     int fd, idx = 0;
     char desc[BUF_SIZE];
@@ -1589,6 +1595,7 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options)
         "ddb.geometry.heads = \"%d\"\n"
         "ddb.geometry.sectors = \"63\"\n"
         "ddb.adapterType = \"%s\"\n";
+    Error *local_err = NULL;
 
     if (filename_decompose(filename, path, prefix, postfix, PATH_MAX)) {
         return -EINVAL;
@@ -1651,8 +1658,10 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options)
     }
     if (backing_file) {
         BlockDriverState *bs = bdrv_new("");
-        ret = bdrv_open(bs, backing_file, NULL, 0, NULL);
+        ret = bdrv_open(bs, backing_file, NULL, 0, NULL, &local_err);
         if (ret != 0) {
+            qerror_report_err(local_err);
+            error_free(local_err);
             bdrv_unref(bs);
             return ret;
         }
index fe4f311d507806785def6b4aaf3a5ebd4ee89b71..db61274332c57017964d16a2b817d74719ca0894 100644 (file)
@@ -155,7 +155,8 @@ static int vpc_probe(const uint8_t *buf, int buf_size, const char *filename)
     return 0;
 }
 
-static int vpc_open(BlockDriverState *bs, QDict *options, int flags)
+static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
+                    Error **errp)
 {
     BDRVVPCState *s = bs->opaque;
     int i;
@@ -683,7 +684,8 @@ static int create_fixed_disk(int fd, uint8_t *buf, int64_t total_size)
     return ret;
 }
 
-static int vpc_create(const char *filename, QEMUOptionParameter *options)
+static int vpc_create(const char *filename, QEMUOptionParameter *options,
+                      Error **errp)
 {
     uint8_t buf[1024];
     struct vhd_footer *footer = (struct vhd_footer *) buf;
index 0129195e29d9af8ec3c4db90436d60a76bb69d48..3ddaa0bcce79f6cf8e07c7a40048f779390edeb8 100644 (file)
@@ -1065,7 +1065,8 @@ static void vvfat_parse_filename(const char *filename, QDict *options,
     qdict_put(options, "rw", qbool_from_int(rw));
 }
 
-static int vvfat_open(BlockDriverState *bs, QDict *options, int flags)
+static int vvfat_open(BlockDriverState *bs, QDict *options, int flags,
+                      Error **errp)
 {
     BDRVVVFATState *s = bs->opaque;
     int cyls, heads, secs;
@@ -2909,6 +2910,7 @@ static int enable_write_target(BDRVVVFATState *s)
 {
     BlockDriver *bdrv_qcow;
     QEMUOptionParameter *options;
+    Error *local_err = NULL;
     int ret;
     int size = sector2cluster(s, s->sector_count);
     s->used_clusters = calloc(size, 1);
@@ -2926,16 +2928,21 @@ static int enable_write_target(BDRVVVFATState *s)
     set_option_parameter_int(options, BLOCK_OPT_SIZE, s->sector_count * 512);
     set_option_parameter(options, BLOCK_OPT_BACKING_FILE, "fat:");
 
-    ret = bdrv_create(bdrv_qcow, s->qcow_filename, options);
+    ret = bdrv_create(bdrv_qcow, s->qcow_filename, options, &local_err);
     if (ret < 0) {
+        qerror_report_err(local_err);
+        error_free(local_err);
         goto err;
     }
 
     s->qcow = bdrv_new("");
 
     ret = bdrv_open(s->qcow, s->qcow_filename, NULL,
-            BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, bdrv_qcow);
+            BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, bdrv_qcow,
+            &local_err);
     if (ret < 0) {
+        qerror_report_err(local_err);
+        error_free(local_err);
         bdrv_unref(s->qcow);
         goto err;
     }
index 07dac05a2cb559964d5efe631597b9944066e722..80605a2bac38be4fc90ed3d449af53168d3a39ec 100644 (file)
@@ -710,17 +710,11 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts,
     }
 
     QINCREF(bs_opts);
-    ret = bdrv_open(dinfo->bdrv, file, bs_opts, bdrv_flags, drv);
+    ret = bdrv_open(dinfo->bdrv, file, bs_opts, bdrv_flags, drv, &error);
 
     if (ret < 0) {
-        if (ret == -EMEDIUMTYPE) {
-            error_report("could not open disk image %s: not in %s format",
-                         file ?: dinfo->id, drv ? drv->format_name :
-                         qdict_get_str(bs_opts, "driver"));
-        } else {
-            error_report("could not open disk image %s: %s",
-                         file ?: dinfo->id, strerror(-ret));
-        }
+        error_report("could not open disk image %s: %s",
+                     file ?: dinfo->id, error_get_pretty(error));
         goto err;
     }
 
@@ -858,6 +852,80 @@ void qmp_blockdev_snapshot_sync(const char *device, const char *snapshot_file,
                        &snapshot, errp);
 }
 
+void qmp_blockdev_snapshot_internal_sync(const char *device,
+                                         const char *name,
+                                         Error **errp)
+{
+    BlockdevSnapshotInternal snapshot = {
+        .device = (char *) device,
+        .name = (char *) name
+    };
+
+    blockdev_do_action(TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC,
+                       &snapshot, errp);
+}
+
+SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device,
+                                                         bool has_id,
+                                                         const char *id,
+                                                         bool has_name,
+                                                         const char *name,
+                                                         Error **errp)
+{
+    BlockDriverState *bs = bdrv_find(device);
+    QEMUSnapshotInfo sn;
+    Error *local_err = NULL;
+    SnapshotInfo *info = NULL;
+    int ret;
+
+    if (!bs) {
+        error_set(errp, QERR_DEVICE_NOT_FOUND, device);
+        return NULL;
+    }
+
+    if (!has_id) {
+        id = NULL;
+    }
+
+    if (!has_name) {
+        name = NULL;
+    }
+
+    if (!id && !name) {
+        error_setg(errp, "Name or id must be provided");
+        return NULL;
+    }
+
+    ret = bdrv_snapshot_find_by_id_and_name(bs, id, name, &sn, &local_err);
+    if (error_is_set(&local_err)) {
+        error_propagate(errp, local_err);
+        return NULL;
+    }
+    if (!ret) {
+        error_setg(errp,
+                   "Snapshot with id '%s' and name '%s' does not exist on "
+                   "device '%s'",
+                   STR_OR_NULL(id), STR_OR_NULL(name), device);
+        return NULL;
+    }
+
+    bdrv_snapshot_delete(bs, id, name, &local_err);
+    if (error_is_set(&local_err)) {
+        error_propagate(errp, local_err);
+        return NULL;
+    }
+
+    info = g_malloc0(sizeof(SnapshotInfo));
+    info->id = g_strdup(sn.id_str);
+    info->name = g_strdup(sn.name);
+    info->date_nsec = sn.date_nsec;
+    info->date_sec = sn.date_sec;
+    info->vm_state_size = sn.vm_state_size;
+    info->vm_clock_nsec = sn.vm_clock_nsec % 1000000000;
+    info->vm_clock_sec = sn.vm_clock_nsec / 1000000000;
+
+    return info;
+}
 
 /* New and old BlockDriverState structs for group snapshots */
 
@@ -889,6 +957,117 @@ struct BlkTransactionState {
     QSIMPLEQ_ENTRY(BlkTransactionState) entry;
 };
 
+/* internal snapshot private data */
+typedef struct InternalSnapshotState {
+    BlkTransactionState common;
+    BlockDriverState *bs;
+    QEMUSnapshotInfo sn;
+} InternalSnapshotState;
+
+static void internal_snapshot_prepare(BlkTransactionState *common,
+                                      Error **errp)
+{
+    const char *device;
+    const char *name;
+    BlockDriverState *bs;
+    QEMUSnapshotInfo old_sn, *sn;
+    bool ret;
+    qemu_timeval tv;
+    BlockdevSnapshotInternal *internal;
+    InternalSnapshotState *state;
+    int ret1;
+
+    g_assert(common->action->kind ==
+             TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC);
+    internal = common->action->blockdev_snapshot_internal_sync;
+    state = DO_UPCAST(InternalSnapshotState, common, common);
+
+    /* 1. parse input */
+    device = internal->device;
+    name = internal->name;
+
+    /* 2. check for validation */
+    bs = bdrv_find(device);
+    if (!bs) {
+        error_set(errp, QERR_DEVICE_NOT_FOUND, device);
+        return;
+    }
+
+    if (!bdrv_is_inserted(bs)) {
+        error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
+        return;
+    }
+
+    if (bdrv_is_read_only(bs)) {
+        error_set(errp, QERR_DEVICE_IS_READ_ONLY, device);
+        return;
+    }
+
+    if (!bdrv_can_snapshot(bs)) {
+        error_set(errp, QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED,
+                  bs->drv->format_name, device, "internal snapshot");
+        return;
+    }
+
+    if (!strlen(name)) {
+        error_setg(errp, "Name is empty");
+        return;
+    }
+
+    /* check whether a snapshot with name exist */
+    ret = bdrv_snapshot_find_by_id_and_name(bs, NULL, name, &old_sn, errp);
+    if (error_is_set(errp)) {
+        return;
+    } else if (ret) {
+        error_setg(errp,
+                   "Snapshot with name '%s' already exists on device '%s'",
+                   name, device);
+        return;
+    }
+
+    /* 3. take the snapshot */
+    sn = &state->sn;
+    pstrcpy(sn->name, sizeof(sn->name), name);
+    qemu_gettimeofday(&tv);
+    sn->date_sec = tv.tv_sec;
+    sn->date_nsec = tv.tv_usec * 1000;
+    sn->vm_clock_nsec = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+
+    ret1 = bdrv_snapshot_create(bs, sn);
+    if (ret1 < 0) {
+        error_setg_errno(errp, -ret1,
+                         "Failed to create snapshot '%s' on device '%s'",
+                         name, device);
+        return;
+    }
+
+    /* 4. succeed, mark a snapshot is created */
+    state->bs = bs;
+}
+
+static void internal_snapshot_abort(BlkTransactionState *common)
+{
+    InternalSnapshotState *state =
+                             DO_UPCAST(InternalSnapshotState, common, common);
+    BlockDriverState *bs = state->bs;
+    QEMUSnapshotInfo *sn = &state->sn;
+    Error *local_error = NULL;
+
+    if (!bs) {
+        return;
+    }
+
+    if (bdrv_snapshot_delete(bs, sn->id_str, sn->name, &local_error) < 0) {
+        error_report("Failed to delete snapshot with id '%s' and name '%s' on "
+                     "device '%s' in abort: %s",
+                     sn->id_str,
+                     sn->name,
+                     bdrv_get_device_name(bs),
+                     error_get_pretty(local_error));
+        error_free(local_error);
+    }
+}
+
 /* external snapshot private data */
 typedef struct ExternalSnapshotState {
     BlkTransactionState common;
@@ -971,9 +1150,9 @@ static void external_snapshot_prepare(BlkTransactionState *common,
     /* TODO Inherit bs->options or only take explicit options with an
      * extended QMP command? */
     ret = bdrv_open(state->new_bs, new_image_file, NULL,
-                    flags | BDRV_O_NO_BACKING, drv);
+                    flags | BDRV_O_NO_BACKING, drv, &local_err);
     if (ret != 0) {
-        error_setg_file_open(errp, -ret, new_image_file);
+        error_propagate(errp, local_err);
     }
 }
 
@@ -1072,6 +1251,11 @@ static const BdrvActionOps actions[] = {
         .prepare = abort_prepare,
         .commit = abort_commit,
     },
+    [TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC] = {
+        .instance_size = sizeof(InternalSnapshotState),
+        .prepare  = internal_snapshot_prepare,
+        .abort = internal_snapshot_abort,
+    },
 };
 
 /*
@@ -1102,6 +1286,8 @@ void qmp_transaction(TransactionActionList *dev_list, Error **errp)
         assert(dev_info->kind < ARRAY_SIZE(actions));
 
         ops = &actions[dev_info->kind];
+        assert(ops->instance_size > 0);
+
         state = g_malloc0(ops->instance_size);
         state->ops = ops;
         state->action = dev_info;
@@ -1203,11 +1389,12 @@ static void qmp_bdrv_open_encrypted(BlockDriverState *bs, const char *filename,
                                     int bdrv_flags, BlockDriver *drv,
                                     const char *password, Error **errp)
 {
+    Error *local_err = NULL;
     int ret;
 
-    ret = bdrv_open(bs, filename, NULL, bdrv_flags, drv);
+    ret = bdrv_open(bs, filename, NULL, bdrv_flags, drv, &local_err);
     if (ret < 0) {
-        error_setg_file_open(errp, -ret, filename);
+        error_propagate(errp, local_err);
         return;
     }
 
@@ -1627,10 +1814,10 @@ void qmp_drive_backup(const char *device, const char *target,
     }
 
     target_bs = bdrv_new("");
-    ret = bdrv_open(target_bs, target, NULL, flags, drv);
+    ret = bdrv_open(target_bs, target, NULL, flags, drv, &local_err);
     if (ret < 0) {
         bdrv_unref(target_bs);
-        error_setg_file_open(errp, -ret, target);
+        error_propagate(errp, local_err);
         return;
     }
 
@@ -1762,10 +1949,11 @@ void qmp_drive_mirror(const char *device, const char *target,
      * file.
      */
     target_bs = bdrv_new("");
-    ret = bdrv_open(target_bs, target, NULL, flags | BDRV_O_NO_BACKING, drv);
+    ret = bdrv_open(target_bs, target, NULL, flags | BDRV_O_NO_BACKING, drv,
+                    &local_err);
     if (ret < 0) {
         bdrv_unref(target_bs);
-        error_setg_file_open(errp, -ret, target);
+        error_propagate(errp, local_err);
         return;
     }
 
index 2b83936e8eb32bf7bbd96fc55278f5bf2fcb0b32..8f307eaee8c776c6d7046bb5e95bf91746a087be 100755 (executable)
--- a/configure
+++ b/configure
@@ -238,6 +238,7 @@ win_sdk="no"
 want_tools="yes"
 libiscsi=""
 coroutine=""
+coroutine_pool=""
 seccomp=""
 glusterfs=""
 glusterfs_discard="no"
@@ -561,7 +562,6 @@ Haiku)
   audio_possible_drivers="oss alsa sdl esd pa"
   linux="yes"
   linux_user="yes"
-  usb="linux"
   kvm="yes"
   vhost_net="yes"
   vhost_scsi="yes"
@@ -574,9 +574,6 @@ esac
 
 if [ "$bsd" = "yes" ] ; then
   if [ "$darwin" != "yes" ] ; then
-    if [ "$targetos" != "FreeBSD" ]; then
-      usb="bsd"
-    fi
     bsd_user="yes"
   fi
 fi
@@ -888,6 +885,10 @@ for opt do
   ;;
   --with-coroutine=*) coroutine="$optarg"
   ;;
+  --disable-coroutine-pool) coroutine_pool="no"
+  ;;
+  --enable-coroutine-pool) coroutine_pool="yes"
+  ;;
   --disable-docs) docs="no"
   ;;
   --enable-docs) docs="yes"
@@ -1189,6 +1190,8 @@ echo "  --disable-seccomp        disable seccomp support"
 echo "  --enable-seccomp         enables seccomp support"
 echo "  --with-coroutine=BACKEND coroutine backend. Supported options:"
 echo "                           gthread, ucontext, sigaltstack, windows"
+echo "  --disable-coroutine-pool disable coroutine freelist (worse performance)"
+echo "  --enable-coroutine-pool  enable coroutine freelist (better performance)"
 echo "  --enable-glusterfs       enable GlusterFS backend"
 echo "  --disable-glusterfs      disable GlusterFS backend"
 echo "  --enable-gcov            enable test coverage analysis with gcov"
@@ -3119,7 +3122,6 @@ fi
 if test "$libusb" != "no" ; then
     if $pkg_config --atleast-version=1.0.13 libusb-1.0; then
         libusb="yes"
-       usb="libusb"
         libusb_cflags=$($pkg_config --cflags libusb-1.0)
         libusb_libs=$($pkg_config --libs libusb-1.0)
         QEMU_CFLAGS="$QEMU_CFLAGS $libusb_cflags"
@@ -3362,6 +3364,17 @@ else
   esac
 fi
 
+if test "$coroutine_pool" = ""; then
+  if test "$coroutine" = "gthread"; then
+    coroutine_pool=no
+  else
+    coroutine_pool=yes
+  fi
+fi
+if test "$coroutine" = "gthread" -a "$coroutine_pool" = "yes"; then
+  error_exit "'gthread' coroutine backend does not support pool (use --disable-coroutine-pool)"
+fi
+
 ##########################################
 # check if we have open_by_handle_at
 
@@ -3733,6 +3746,7 @@ echo "build guest agent $guest_agent"
 echo "QGA VSS support   $guest_agent_with_vss"
 echo "seccomp support   $seccomp"
 echo "coroutine backend $coroutine"
+echo "coroutine pool    $coroutine_pool"
 echo "GlusterFS support $glusterfs"
 echo "virtio-blk-data-plane $virtio_blk_data_plane"
 echo "gcov              $gcov_tool"
@@ -4092,6 +4106,11 @@ if test "$rbd" = "yes" ; then
 fi
 
 echo "CONFIG_COROUTINE_BACKEND=$coroutine" >> $config_host_mak
+if test "$coroutine_pool" = "yes" ; then
+  echo "CONFIG_COROUTINE_POOL=1" >> $config_host_mak
+else
+  echo "CONFIG_COROUTINE_POOL=0" >> $config_host_mak
+fi
 
 if test "$open_by_handle_at" = "yes" ; then
   echo "CONFIG_OPEN_BY_HANDLE=y" >> $config_host_mak
@@ -4142,24 +4161,11 @@ if test "$virtio_blk_data_plane" = "yes" ; then
 fi
 
 # USB host support
-case "$usb" in
-linux)
-  echo "HOST_USB=linux legacy" >> $config_host_mak
-;;
-bsd)
-  echo "HOST_USB=bsd" >> $config_host_mak
-;;
-libusb)
-  if test "$linux" = "yes"; then
-    echo "HOST_USB=libusb linux legacy" >> $config_host_mak
-  else
-    echo "HOST_USB=libusb legacy" >> $config_host_mak
-  fi
-;;
-*)
+if test "$libusb" = "yes"; then
+  echo "HOST_USB=libusb legacy" >> $config_host_mak
+else
   echo "HOST_USB=stub" >> $config_host_mak
-;;
-esac
+fi
 
 # TPM passthrough support?
 if test "$tpm" = "yes"; then
@@ -4651,7 +4657,7 @@ if [ "$dtc_internal" = "yes" ]; then
 fi
 
 # build tree in object directory in case the source is not in the current directory
-DIRS="tests tests/tcg tests/tcg/cris tests/tcg/lm32 tests/libqos tests/qapi-schema tests/tcg/xtensa"
+DIRS="tests tests/tcg tests/tcg/cris tests/tcg/lm32 tests/libqos tests/qapi-schema tests/tcg/xtensa tests/qemu-iotests"
 DIRS="$DIRS pc-bios/optionrom pc-bios/spapr-rtas pc-bios/s390-ccw"
 DIRS="$DIRS roms/seabios roms/vgabios"
 DIRS="$DIRS qapi-generated"
diff --git a/docs/qmp/README b/docs/qmp/README
new file mode 100644 (file)
index 0000000..85c4bc1
--- /dev/null
@@ -0,0 +1,87 @@
+                          QEMU Machine Protocol
+                          =====================
+
+Introduction
+------------
+
+The QEMU Machine Protocol (QMP) allows applications to operate a
+QEMU instance.
+
+QMP is JSON[1] based and features the following:
+
+- Lightweight, text-based, easy to parse data format
+- Asynchronous messages support (ie. events)
+- Capabilities Negotiation
+
+For detailed information on QMP's usage, please, refer to the following files:
+
+o qmp-spec.txt      QEMU Machine Protocol current specification
+o qmp-commands.txt  QMP supported commands (auto-generated at build-time)
+o qmp-events.txt    List of available asynchronous events
+
+[1] http://www.json.org
+
+Usage
+-----
+
+You can use the -qmp option to enable QMP. For example, the following
+makes QMP available on localhost port 4444:
+
+$ qemu [...] -qmp tcp:localhost:4444,server,nowait
+
+However, for more flexibility and to make use of more options, the -mon
+command-line option should be used. For instance, the following example
+creates one HMP instance (human monitor) on stdio and one QMP instance
+on localhost port 4444:
+
+$ qemu [...] -chardev stdio,id=mon0 -mon chardev=mon0,mode=readline \
+             -chardev socket,id=mon1,host=localhost,port=4444,server,nowait \
+             -mon chardev=mon1,mode=control,pretty=on
+
+Please, refer to QEMU's manpage for more information.
+
+Simple Testing
+--------------
+
+To manually test QMP one can connect with telnet and issue commands by hand:
+
+$ telnet localhost 4444
+Trying 127.0.0.1...
+Connected to localhost.
+Escape character is '^]'.
+{
+    "QMP": {
+        "version": {
+            "qemu": {
+                "micro": 50, 
+                "minor": 6, 
+                "major": 1
+            }, 
+            "package": ""
+        }, 
+        "capabilities": [
+        ]
+    }
+}
+
+{ "execute": "qmp_capabilities" }
+{
+    "return": {
+    }
+}
+
+{ "execute": "query-status" }
+{
+    "return": {
+        "status": "prelaunch", 
+        "singlestep": false, 
+        "running": false
+    }
+}
+
+Please, refer to the qapi-schema.json file for a complete command reference.
+
+QMP wiki page
+-------------
+
+http://wiki.qemu.org/QMP
diff --git a/docs/qmp/qmp-events.txt b/docs/qmp/qmp-events.txt
new file mode 100644 (file)
index 0000000..6b87e97
--- /dev/null
@@ -0,0 +1,502 @@
+                   QEMU Machine Protocol Events
+                   ============================
+
+BALLOON_CHANGE
+--------------
+
+Emitted when the guest changes the actual BALLOON level. This
+value is equivalent to the 'actual' field return by the
+'query-balloon' command
+
+Data:
+
+- "actual": actual level of the guest memory balloon in bytes (json-number)
+
+Example:
+
+{ "event": "BALLOON_CHANGE",
+    "data": { "actual": 944766976 },
+    "timestamp": { "seconds": 1267020223, "microseconds": 435656 } }
+
+BLOCK_IMAGE_CORRUPTED
+---------------------
+
+Emitted when a disk image is being marked corrupt.
+
+Data:
+
+- "device": Device name (json-string)
+- "msg":    Informative message (e.g., reason for the corruption) (json-string)
+- "offset": If the corruption resulted from an image access, this is the access
+            offset into the image (json-int)
+- "size":   If the corruption resulted from an image access, this is the access
+            size (json-int)
+
+Example:
+
+{ "event": "BLOCK_IMAGE_CORRUPTED",
+    "data": { "device": "ide0-hd0",
+        "msg": "Prevented active L1 table overwrite", "offset": 196608,
+        "size": 65536 },
+    "timestamp": { "seconds": 1378126126, "microseconds": 966463 } }
+
+BLOCK_IO_ERROR
+--------------
+
+Emitted when a disk I/O error occurs.
+
+Data:
+
+- "device": device name (json-string)
+- "operation": I/O operation (json-string, "read" or "write")
+- "action": action that has been taken, it's one of the following (json-string):
+    "ignore": error has been ignored
+    "report": error has been reported to the device
+    "stop": error caused VM to be stopped
+
+Example:
+
+{ "event": "BLOCK_IO_ERROR",
+    "data": { "device": "ide0-hd1",
+              "operation": "write",
+              "action": "stop" },
+    "timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
+
+Note: If action is "stop", a STOP event will eventually follow the
+BLOCK_IO_ERROR event.
+
+BLOCK_JOB_CANCELLED
+-------------------
+
+Emitted when a block job has been cancelled.
+
+Data:
+
+- "type":     Job type (json-string; "stream" for image streaming
+                                     "commit" for block commit)
+- "device":   Device name (json-string)
+- "len":      Maximum progress value (json-int)
+- "offset":   Current progress value (json-int)
+              On success this is equal to len.
+              On failure this is less than len.
+- "speed":    Rate limit, bytes per second (json-int)
+
+Example:
+
+{ "event": "BLOCK_JOB_CANCELLED",
+     "data": { "type": "stream", "device": "virtio-disk0",
+               "len": 10737418240, "offset": 134217728,
+               "speed": 0 },
+     "timestamp": { "seconds": 1267061043, "microseconds": 959568 } }
+
+BLOCK_JOB_COMPLETED
+-------------------
+
+Emitted when a block job has completed.
+
+Data:
+
+- "type":     Job type (json-string; "stream" for image streaming
+                                     "commit" for block commit)
+- "device":   Device name (json-string)
+- "len":      Maximum progress value (json-int)
+- "offset":   Current progress value (json-int)
+              On success this is equal to len.
+              On failure this is less than len.
+- "speed":    Rate limit, bytes per second (json-int)
+- "error":    Error message (json-string, optional)
+              Only present on failure.  This field contains a human-readable
+              error message.  There are no semantics other than that streaming
+              has failed and clients should not try to interpret the error
+              string.
+
+Example:
+
+{ "event": "BLOCK_JOB_COMPLETED",
+     "data": { "type": "stream", "device": "virtio-disk0",
+               "len": 10737418240, "offset": 10737418240,
+               "speed": 0 },
+     "timestamp": { "seconds": 1267061043, "microseconds": 959568 } }
+
+BLOCK_JOB_ERROR
+---------------
+
+Emitted when a block job encounters an error.
+
+Data:
+
+- "device": device name (json-string)
+- "operation": I/O operation (json-string, "read" or "write")
+- "action": action that has been taken, it's one of the following (json-string):
+    "ignore": error has been ignored, the job may fail later
+    "report": error will be reported and the job canceled
+    "stop": error caused job to be paused
+
+Example:
+
+{ "event": "BLOCK_JOB_ERROR",
+    "data": { "device": "ide0-hd1",
+              "operation": "write",
+              "action": "stop" },
+    "timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
+
+BLOCK_JOB_READY
+---------------
+
+Emitted when a block job is ready to complete.
+
+Data:
+
+- "device": device name (json-string)
+
+Example:
+
+{ "event": "BLOCK_JOB_READY",
+    "data": { "device": "ide0-hd1" },
+    "timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
+
+Note: The "ready to complete" status is always reset by a BLOCK_JOB_ERROR
+event.
+
+DEVICE_DELETED
+--------------
+
+Emitted whenever the device removal completion is acknowledged
+by the guest.
+At this point, it's safe to reuse the specified device ID.
+Device removal can be initiated by the guest or by HMP/QMP commands.
+
+Data:
+
+- "device": device name (json-string, optional)
+- "path": device path (json-string)
+
+{ "event": "DEVICE_DELETED",
+  "data": { "device": "virtio-net-pci-0",
+            "path": "/machine/peripheral/virtio-net-pci-0" },
+  "timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
+
+DEVICE_TRAY_MOVED
+-----------------
+
+It's emitted whenever the tray of a removable device is moved by the guest
+or by HMP/QMP commands.
+
+Data:
+
+- "device": device name (json-string)
+- "tray-open": true if the tray has been opened or false if it has been closed
+               (json-bool)
+
+{ "event": "DEVICE_TRAY_MOVED",
+  "data": { "device": "ide1-cd0",
+            "tray-open": true
+  },
+  "timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
+
+GUEST_PANICKED
+--------------
+
+Emitted when guest OS panic is detected.
+
+Data:
+
+- "action": Action that has been taken (json-string, currently always "pause").
+
+Example:
+
+{ "event": "GUEST_PANICKED",
+     "data": { "action": "pause" } }
+
+NIC_RX_FILTER_CHANGED
+---------------------
+
+The event is emitted once until the query command is executed,
+the first event will always be emitted.
+
+Data:
+
+- "name": net client name (json-string)
+- "path": device path (json-string)
+
+{ "event": "NIC_RX_FILTER_CHANGED",
+  "data": { "name": "vnet0",
+            "path": "/machine/peripheral/vnet0/virtio-backend" },
+  "timestamp": { "seconds": 1368697518, "microseconds": 326866 } }
+}
+
+RESET
+-----
+
+Emitted when the Virtual Machine is reseted.
+
+Data: None.
+
+Example:
+
+{ "event": "RESET",
+    "timestamp": { "seconds": 1267041653, "microseconds": 9518 } }
+
+RESUME
+------
+
+Emitted when the Virtual Machine resumes execution.
+
+Data: None.
+
+Example:
+
+{ "event": "RESUME",
+    "timestamp": { "seconds": 1271770767, "microseconds": 582542 } }
+
+RTC_CHANGE
+----------
+
+Emitted when the guest changes the RTC time.
+
+Data:
+
+- "offset": Offset between base RTC clock (as specified by -rtc base), and
+new RTC clock value (json-number)
+
+Example:
+
+{ "event": "RTC_CHANGE",
+    "data": { "offset": 78 },
+    "timestamp": { "seconds": 1267020223, "microseconds": 435656 } }
+
+SHUTDOWN
+--------
+
+Emitted when the Virtual Machine is powered down.
+
+Data: None.
+
+Example:
+
+{ "event": "SHUTDOWN",
+    "timestamp": { "seconds": 1267040730, "microseconds": 682951 } }
+
+Note: If the command-line option "-no-shutdown" has been specified, a STOP
+event will eventually follow the SHUTDOWN event.
+
+SPICE_CONNECTED, SPICE_DISCONNECTED
+-----------------------------------
+
+Emitted when a SPICE client connects or disconnects.
+
+Data:
+
+- "server": Server information (json-object)
+  - "host": IP address (json-string)
+  - "port": port number (json-string)
+  - "family": address family (json-string, "ipv4" or "ipv6")
+- "client": Client information (json-object)
+  - "host": IP address (json-string)
+  - "port": port number (json-string)
+  - "family": address family (json-string, "ipv4" or "ipv6")
+
+Example:
+
+{ "timestamp": {"seconds": 1290688046, "microseconds": 388707},
+  "event": "SPICE_CONNECTED",
+  "data": {
+    "server": { "port": "5920", "family": "ipv4", "host": "127.0.0.1"},
+    "client": {"port": "52873", "family": "ipv4", "host": "127.0.0.1"}
+}}
+
+SPICE_INITIALIZED
+-----------------
+
+Emitted after initial handshake and authentication takes place (if any)
+and the SPICE channel is up'n'running
+
+Data:
+
+- "server": Server information (json-object)
+  - "host": IP address (json-string)
+  - "port": port number (json-string)
+  - "family": address family (json-string, "ipv4" or "ipv6")
+  - "auth": authentication method (json-string, optional)
+- "client": Client information (json-object)
+  - "host": IP address (json-string)
+  - "port": port number (json-string)
+  - "family": address family (json-string, "ipv4" or "ipv6")
+  - "connection-id": spice connection id.  All channels with the same id
+                     belong to the same spice session (json-int)
+  - "channel-type": channel type.  "1" is the main control channel, filter for
+                    this one if you want track spice sessions only (json-int)
+  - "channel-id": channel id.  Usually "0", might be different needed when
+                  multiple channels of the same type exist, such as multiple
+                  display channels in a multihead setup (json-int)
+  - "tls": whevener the channel is encrypted (json-bool)
+
+Example:
+
+{ "timestamp": {"seconds": 1290688046, "microseconds": 417172},
+  "event": "SPICE_INITIALIZED",
+  "data": {"server": {"auth": "spice", "port": "5921",
+                      "family": "ipv4", "host": "127.0.0.1"},
+           "client": {"port": "49004", "family": "ipv4", "channel-type": 3,
+                      "connection-id": 1804289383, "host": "127.0.0.1",
+                      "channel-id": 0, "tls": true}
+}}
+
+STOP
+----
+
+Emitted when the Virtual Machine is stopped.
+
+Data: None.
+
+Example:
+
+{ "event": "STOP",
+    "timestamp": { "seconds": 1267041730, "microseconds": 281295 } }
+
+SUSPEND
+-------
+
+Emitted when guest enters S3 state.
+
+Data: None.
+
+Example:
+
+{ "event": "SUSPEND",
+     "timestamp": { "seconds": 1344456160, "microseconds": 309119 } }
+
+SUSPEND_DISK
+------------
+
+Emitted when the guest makes a request to enter S4 state.
+
+Data: None.
+
+Example:
+
+{ "event": "SUSPEND_DISK",
+     "timestamp": { "seconds": 1344456160, "microseconds": 309119 } }
+
+Note: QEMU shuts down when entering S4 state.
+
+VNC_CONNECTED
+-------------
+
+Emitted when a VNC client establishes a connection.
+
+Data:
+
+- "server": Server information (json-object)
+  - "host": IP address (json-string)
+  - "service": port number (json-string)
+  - "family": address family (json-string, "ipv4" or "ipv6")
+  - "auth": authentication method (json-string, optional)
+- "client": Client information (json-object)
+  - "host": IP address (json-string)
+  - "service": port number (json-string)
+  - "family": address family (json-string, "ipv4" or "ipv6")
+
+Example:
+
+{ "event": "VNC_CONNECTED",
+    "data": {
+        "server": { "auth": "sasl", "family": "ipv4",
+                    "service": "5901", "host": "0.0.0.0" },
+        "client": { "family": "ipv4", "service": "58425",
+                    "host": "127.0.0.1" } },
+    "timestamp": { "seconds": 1262976601, "microseconds": 975795 } }
+
+
+Note: This event is emitted before any authentication takes place, thus
+the authentication ID is not provided.
+
+VNC_DISCONNECTED
+----------------
+
+Emitted when the connection is closed.
+
+Data:
+
+- "server": Server information (json-object)
+  - "host": IP address (json-string)
+  - "service": port number (json-string)
+  - "family": address family (json-string, "ipv4" or "ipv6")
+  - "auth": authentication method (json-string, optional)
+- "client": Client information (json-object)
+  - "host": IP address (json-string)
+  - "service": port number (json-string)
+  - "family": address family (json-string, "ipv4" or "ipv6")
+  - "x509_dname": TLS dname (json-string, optional)
+  - "sasl_username": SASL username (json-string, optional)
+
+Example:
+
+{ "event": "VNC_DISCONNECTED",
+    "data": {
+        "server": { "auth": "sasl", "family": "ipv4",
+                    "service": "5901", "host": "0.0.0.0" },
+        "client": { "family": "ipv4", "service": "58425",
+                    "host": "127.0.0.1", "sasl_username": "luiz" } },
+    "timestamp": { "seconds": 1262976601, "microseconds": 975795 } }
+
+VNC_INITIALIZED
+---------------
+
+Emitted after authentication takes place (if any) and the VNC session is
+made active.
+
+Data:
+
+- "server": Server information (json-object)
+  - "host": IP address (json-string)
+  - "service": port number (json-string)
+  - "family": address family (json-string, "ipv4" or "ipv6")
+  - "auth": authentication method (json-string, optional)
+- "client": Client information (json-object)
+  - "host": IP address (json-string)
+  - "service": port number (json-string)
+  - "family": address family (json-string, "ipv4" or "ipv6")
+  - "x509_dname": TLS dname (json-string, optional)
+  - "sasl_username": SASL username (json-string, optional)
+
+Example:
+
+{ "event": "VNC_INITIALIZED",
+    "data": {
+        "server": { "auth": "sasl", "family": "ipv4",
+                    "service": "5901", "host": "0.0.0.0"},
+        "client": { "family": "ipv4", "service": "46089",
+                    "host": "127.0.0.1", "sasl_username": "luiz" } },
+        "timestamp": { "seconds": 1263475302, "microseconds": 150772 } }
+
+WAKEUP
+------
+
+Emitted when the guest has woken up from S3 and is running.
+
+Data: None.
+
+Example:
+
+{ "event": "WATCHDOG",
+     "timestamp": { "seconds": 1344522075, "microseconds": 745528 } }
+
+WATCHDOG
+--------
+
+Emitted when the watchdog device's timer is expired.
+
+Data:
+
+- "action": Action that has been taken, it's one of the following (json-string):
+            "reset", "shutdown", "poweroff", "pause", "debug", or "none"
+
+Example:
+
+{ "event": "WATCHDOG",
+     "data": { "action": "reset" },
+     "timestamp": { "seconds": 1267061043, "microseconds": 959568 } }
+
+Note: If action is "reset", "shutdown", or "pause" the WATCHDOG event is
+followed respectively by the RESET, SHUTDOWN, or STOP events.
diff --git a/docs/qmp/qmp-spec.txt b/docs/qmp/qmp-spec.txt
new file mode 100644 (file)
index 0000000..22568c6
--- /dev/null
@@ -0,0 +1,273 @@
+                      QEMU Machine Protocol Specification
+
+1. Introduction
+===============
+
+This document specifies the QEMU Machine Protocol (QMP), a JSON-based protocol
+which is available for applications to operate QEMU at the machine-level.
+
+2. Protocol Specification
+=========================
+
+This section details the protocol format. For the purpose of this document
+"Client" is any application which is using QMP to communicate with QEMU and
+"Server" is QEMU itself.
+
+JSON data structures, when mentioned in this document, are always in the
+following format:
+
+    json-DATA-STRUCTURE-NAME
+
+Where DATA-STRUCTURE-NAME is any valid JSON data structure, as defined by
+the JSON standard:
+
+http://www.ietf.org/rfc/rfc4627.txt
+
+For convenience, json-object members and json-array elements mentioned in
+this document will be in a certain order. However, in real protocol usage
+they can be in ANY order, thus no particular order should be assumed.
+
+2.1 General Definitions
+-----------------------
+
+2.1.1 All interactions transmitted by the Server are json-objects, always
+      terminating with CRLF
+
+2.1.2 All json-objects members are mandatory when not specified otherwise
+
+2.2 Server Greeting
+-------------------
+
+Right when connected the Server will issue a greeting message, which signals
+that the connection has been successfully established and that the Server is
+ready for capabilities negotiation (for more information refer to section
+'4. Capabilities Negotiation').
+
+The greeting message format is:
+
+{ "QMP": { "version": json-object, "capabilities": json-array } }
+
+ Where,
+
+- The "version" member contains the Server's version information (the format
+  is the same of the query-version command)
+- The "capabilities" member specify the availability of features beyond the
+  baseline specification
+
+2.3 Issuing Commands
+--------------------
+
+The format for command execution is:
+
+{ "execute": json-string, "arguments": json-object, "id": json-value }
+
+ Where,
+
+- The "execute" member identifies the command to be executed by the Server
+- The "arguments" member is used to pass any arguments required for the
+  execution of the command, it is optional when no arguments are required
+- The "id" member is a transaction identification associated with the
+  command execution, it is optional and will be part of the response if
+  provided
+
+2.4 Commands Responses
+----------------------
+
+There are two possible responses which the Server will issue as the result
+of a command execution: success or error.
+
+2.4.1 success
+-------------
+
+The format of a success response is:
+
+{ "return": json-object, "id": json-value }
+
+ Where,
+
+- The "return" member contains the command returned data, which is defined
+  in a per-command basis or an empty json-object if the command does not
+  return data
+- The "id" member contains the transaction identification associated
+  with the command execution if issued by the Client
+
+2.4.2 error
+-----------
+
+The format of an error response is:
+
+{ "error": { "class": json-string, "desc": json-string }, "id": json-value }
+
+ Where,
+
+- The "class" member contains the error class name (eg. "GenericError")
+- The "desc" member is a human-readable error message. Clients should
+  not attempt to parse this message.
+- The "id" member contains the transaction identification associated with
+  the command execution if issued by the Client
+
+NOTE: Some errors can occur before the Server is able to read the "id" member,
+in these cases the "id" member will not be part of the error response, even
+if provided by the client.
+
+2.5 Asynchronous events
+-----------------------
+
+As a result of state changes, the Server may send messages unilaterally
+to the Client at any time. They are called "asynchronous events".
+
+The format of asynchronous events is:
+
+{ "event": json-string, "data": json-object,
+  "timestamp": { "seconds": json-number, "microseconds": json-number } }
+
+ Where,
+
+- The "event" member contains the event's name
+- The "data" member contains event specific data, which is defined in a
+  per-event basis, it is optional
+- The "timestamp" member contains the exact time of when the event occurred
+  in the Server. It is a fixed json-object with time in seconds and
+  microseconds
+
+For a listing of supported asynchronous events, please, refer to the
+qmp-events.txt file.
+
+3. QMP Examples
+===============
+
+This section provides some examples of real QMP usage, in all of them
+"C" stands for "Client" and "S" stands for "Server".
+
+3.1 Server greeting
+-------------------
+
+S: { "QMP": { "version": { "qemu": { "micro": 50, "minor": 6, "major": 1 },
+     "package": ""}, "capabilities": []}}
+
+3.2 Simple 'stop' execution
+---------------------------
+
+C: { "execute": "stop" }
+S: { "return": {} }
+
+3.3 KVM information
+-------------------
+
+C: { "execute": "query-kvm", "id": "example" }
+S: { "return": { "enabled": true, "present": true }, "id": "example"}
+
+3.4 Parsing error
+------------------
+
+C: { "execute": }
+S: { "error": { "class": "GenericError", "desc": "Invalid JSON syntax" } }
+
+3.5 Powerdown event
+-------------------
+
+S: { "timestamp": { "seconds": 1258551470, "microseconds": 802384 },
+    "event": "POWERDOWN" }
+
+4. Capabilities Negotiation
+----------------------------
+
+When a Client successfully establishes a connection, the Server is in
+Capabilities Negotiation mode.
+
+In this mode only the qmp_capabilities command is allowed to run, all
+other commands will return the CommandNotFound error. Asynchronous
+messages are not delivered either.
+
+Clients should use the qmp_capabilities command to enable capabilities
+advertised in the Server's greeting (section '2.2 Server Greeting') they
+support.
+
+When the qmp_capabilities command is issued, and if it does not return an
+error, the Server enters in Command mode where capabilities changes take
+effect, all commands (except qmp_capabilities) are allowed and asynchronous
+messages are delivered.
+
+5 Compatibility Considerations
+------------------------------
+
+All protocol changes or new features which modify the protocol format in an
+incompatible way are disabled by default and will be advertised by the
+capabilities array (section '2.2 Server Greeting'). Thus, Clients can check
+that array and enable the capabilities they support.
+
+The QMP Server performs a type check on the arguments to a command.  It
+generates an error if a value does not have the expected type for its
+key, or if it does not understand a key that the Client included.  The
+strictness of the Server catches wrong assumptions of Clients about
+the Server's schema.  Clients can assume that, when such validation
+errors occur, they will be reported before the command generated any
+side effect.
+
+However, Clients must not assume any particular:
+
+- Length of json-arrays
+- Size of json-objects; in particular, future versions of QEMU may add
+  new keys and Clients should be able to ignore them.
+- Order of json-object members or json-array elements
+- Amount of errors generated by a command, that is, new errors can be added
+  to any existing command in newer versions of the Server
+
+Of course, the Server does guarantee to send valid JSON.  But apart from
+this, a Client should be "conservative in what they send, and liberal in
+what they accept".
+
+6. Downstream extension of QMP
+------------------------------
+
+We recommend that downstream consumers of QEMU do *not* modify QMP.
+Management tools should be able to support both upstream and downstream
+versions of QMP without special logic, and downstream extensions are
+inherently at odds with that.
+
+However, we recognize that it is sometimes impossible for downstreams to
+avoid modifying QMP.  Both upstream and downstream need to take care to
+preserve long-term compatibility and interoperability.
+
+To help with that, QMP reserves JSON object member names beginning with
+'__' (double underscore) for downstream use ("downstream names").  This
+means upstream will never use any downstream names for its commands,
+arguments, errors, asynchronous events, and so forth.
+
+Any new names downstream wishes to add must begin with '__'.  To
+ensure compatibility with other downstreams, it is strongly
+recommended that you prefix your downstream names with '__RFQDN_' where
+RFQDN is a valid, reverse fully qualified domain name which you
+control.  For example, a qemu-kvm specific monitor command would be:
+
+    (qemu) __org.linux-kvm_enable_irqchip
+
+Downstream must not change the server greeting (section 2.2) other than
+to offer additional capabilities.  But see below for why even that is
+discouraged.
+
+Section '5 Compatibility Considerations' applies to downstream as well
+as to upstream, obviously.  It follows that downstream must behave
+exactly like upstream for any input not containing members with
+downstream names ("downstream members"), except it may add members
+with downstream names to its output.
+
+Thus, a client should not be able to distinguish downstream from
+upstream as long as it doesn't send input with downstream members, and
+properly ignores any downstream members in the output it receives.
+
+Advice on downstream modifications:
+
+1. Introducing new commands is okay.  If you want to extend an existing
+   command, consider introducing a new one with the new behaviour
+   instead.
+
+2. Introducing new asynchronous messages is okay.  If you want to extend
+   an existing message, consider adding a new one instead.
+
+3. Introducing new errors for use in new commands is okay.  Adding new
+   errors to existing commands counts as extension, so 1. applies.
+
+4. New capabilities are strongly discouraged.  Capabilities are for
+   evolving the basic protocol, and multiple diverging basic protocol
+   dialects are most undesirable.
diff --git a/exec.c b/exec.c
index 5e6015cbd3af575342988ccda5e6c26fb1754b6d..8f5e7abfa70cedc771f6301ab038cb3d279f0758 100644 (file)
--- a/exec.c
+++ b/exec.c
@@ -749,6 +749,18 @@ static int subpage_register (subpage_t *mmio, uint32_t start, uint32_t end,
                              uint16_t section);
 static subpage_t *subpage_init(AddressSpace *as, hwaddr base);
 
+static void *(*phys_mem_alloc)(ram_addr_t size) = qemu_anon_ram_alloc;
+
+/*
+ * Set a custom physical guest memory alloator.
+ * Accelerators with unusual needs may need this.  Hopefully, we can
+ * get rid of it eventually.
+ */
+void phys_mem_set_alloc(void *(*alloc)(ram_addr_t))
+{
+    phys_mem_alloc = alloc;
+}
+
 static uint16_t phys_section_add(MemoryRegionSection *section)
 {
     /* The physical section number is ORed with a page-aligned
@@ -880,7 +892,7 @@ void qemu_mutex_unlock_ramlist(void)
     qemu_mutex_unlock(&ram_list.mutex);
 }
 
-#if defined(__linux__) && !defined(TARGET_S390X)
+#ifdef __linux__
 
 #include <sys/vfs.h>
 
@@ -983,6 +995,14 @@ static void *file_ram_alloc(RAMBlock *block,
     block->fd = fd;
     return area;
 }
+#else
+static void *file_ram_alloc(RAMBlock *block,
+                            ram_addr_t memory,
+                            const char *path)
+{
+    fprintf(stderr, "-mem-path not supported on this host\n");
+    exit(1);
+}
 #endif
 
 static ram_addr_t find_ram_offset(ram_addr_t size)
@@ -1099,6 +1119,7 @@ ram_addr_t qemu_ram_alloc_from_ptr(ram_addr_t size, void *host,
 
     size = TARGET_PAGE_ALIGN(size);
     new_block = g_malloc0(sizeof(*new_block));
+    new_block->fd = -1;
 
     /* This assumes the iothread lock is taken here too.  */
     qemu_mutex_lock_ramlist();
@@ -1107,26 +1128,32 @@ ram_addr_t qemu_ram_alloc_from_ptr(ram_addr_t size, void *host,
     if (host) {
         new_block->host = host;
         new_block->flags |= RAM_PREALLOC_MASK;
+    } else if (xen_enabled()) {
+        if (mem_path) {
+            fprintf(stderr, "-mem-path not supported with Xen\n");
+            exit(1);
+        }
+        xen_ram_alloc(new_block->offset, size, mr);
     } else {
         if (mem_path) {
-#if defined (__linux__) && !defined(TARGET_S390X)
+            if (phys_mem_alloc != qemu_anon_ram_alloc) {
+                /*
+                 * file_ram_alloc() needs to allocate just like
+                 * phys_mem_alloc, but we haven't bothered to provide
+                 * a hook there.
+                 */
+                fprintf(stderr,
+                        "-mem-path not supported with this accelerator\n");
+                exit(1);
+            }
             new_block->host = file_ram_alloc(new_block, size, mem_path);
+        }
+        if (!new_block->host) {
+            new_block->host = phys_mem_alloc(size);
             if (!new_block->host) {
-                new_block->host = qemu_anon_ram_alloc(size);
-                memory_try_enable_merging(new_block->host, size);
-            }
-#else
-            fprintf(stderr, "-mem-path option unsupported\n");
-            exit(1);
-#endif
-        } else {
-            if (xen_enabled()) {
-                xen_ram_alloc(new_block->offset, size, mr);
-            } else if (kvm_enabled()) {
-                /* some s390/kvm configurations have special constraints */
-                new_block->host = kvm_ram_alloc(size);
-            } else {
-                new_block->host = qemu_anon_ram_alloc(size);
+                fprintf(stderr, "Cannot set up guest memory '%s': %s\n",
+                        new_block->mr->name, strerror(errno));
+                exit(1);
             }
             memory_try_enable_merging(new_block->host, size);
         }
@@ -1201,23 +1228,13 @@ void qemu_ram_free(ram_addr_t addr)
             ram_list.version++;
             if (block->flags & RAM_PREALLOC_MASK) {
                 ;
-            } else if (mem_path) {
-#if defined (__linux__) && !defined(TARGET_S390X)
-                if (block->fd) {
-                    munmap(block->host, block->length);
-                    close(block->fd);
-                } else {
-                    qemu_anon_ram_free(block->host, block->length);
-                }
-#else
-                abort();
-#endif
+            } else if (xen_enabled()) {
+                xen_invalidate_map_cache_entry(block->host);
+            } else if (block->fd >= 0) {
+                munmap(block->host, block->length);
+                close(block->fd);
             } else {
-                if (xen_enabled()) {
-                    xen_invalidate_map_cache_entry(block->host);
-                } else {
-                    qemu_anon_ram_free(block->host, block->length);
-                }
+                qemu_anon_ram_free(block->host, block->length);
             }
             g_free(block);
             break;
@@ -1241,38 +1258,31 @@ void qemu_ram_remap(ram_addr_t addr, ram_addr_t length)
             vaddr = block->host + offset;
             if (block->flags & RAM_PREALLOC_MASK) {
                 ;
+            } else if (xen_enabled()) {
+                abort();
             } else {
                 flags = MAP_FIXED;
                 munmap(vaddr, length);
-                if (mem_path) {
-#if defined(__linux__) && !defined(TARGET_S390X)
-                    if (block->fd) {
+                if (block->fd >= 0) {
 #ifdef MAP_POPULATE
-                        flags |= mem_prealloc ? MAP_POPULATE | MAP_SHARED :
-                            MAP_PRIVATE;
+                    flags |= mem_prealloc ? MAP_POPULATE | MAP_SHARED :
+                        MAP_PRIVATE;
 #else
-                        flags |= MAP_PRIVATE;
-#endif
-                        area = mmap(vaddr, length, PROT_READ | PROT_WRITE,
-                                    flags, block->fd, offset);
-                    } else {
-                        flags |= MAP_PRIVATE | MAP_ANONYMOUS;
-                        area = mmap(vaddr, length, PROT_READ | PROT_WRITE,
-                                    flags, -1, 0);
-                    }
-#else
-                    abort();
+                    flags |= MAP_PRIVATE;
 #endif
+                    area = mmap(vaddr, length, PROT_READ | PROT_WRITE,
+                                flags, block->fd, offset);
                 } else {
-#if defined(TARGET_S390X) && defined(CONFIG_KVM)
-                    flags |= MAP_SHARED | MAP_ANONYMOUS;
-                    area = mmap(vaddr, length, PROT_EXEC|PROT_READ|PROT_WRITE,
-                                flags, -1, 0);
-#else
+                    /*
+                     * Remap needs to match alloc.  Accelerators that
+                     * set phys_mem_alloc never remap.  If they did,
+                     * we'd need a remap hook here.
+                     */
+                    assert(phys_mem_alloc == qemu_anon_ram_alloc);
+
                     flags |= MAP_PRIVATE | MAP_ANONYMOUS;
                     area = mmap(vaddr, length, PROT_READ | PROT_WRITE,
                                 flags, -1, 0);
-#endif
                 }
                 if (area != vaddr) {
                     fprintf(stderr, "Could not remap addr: "
index 65b7f6076c9760a1a69bfa6ec8c2edb6762b6abe..caae5ad9e9e1b2b56912da864718333c1d866154 100644 (file)
@@ -1023,8 +1023,7 @@ ETEXI
                       "of device. If a new image file is specified, the\n\t\t\t"
                       "new image file will become the new root image.\n\t\t\t"
                       "If format is specified, the snapshot file will\n\t\t\t"
-                      "be created in that format. Otherwise the\n\t\t\t"
-                      "snapshot will be internal! (currently unsupported).\n\t\t\t"
+                      "be created in that format.\n\t\t\t"
                       "The default format is qcow2.  The -n flag requests QEMU\n\t\t\t"
                       "to reuse the image found in new-image-file, instead of\n\t\t\t"
                       "recreating it from scratch.",
@@ -1035,6 +1034,40 @@ STEXI
 @item snapshot_blkdev
 @findex snapshot_blkdev
 Snapshot device, using snapshot file as target if provided
+ETEXI
+
+    {
+        .name       = "snapshot_blkdev_internal",
+        .args_type  = "device:B,name:s",
+        .params     = "device name",
+        .help       = "take an internal snapshot of device.\n\t\t\t"
+                      "The format of the image used by device must\n\t\t\t"
+                      "support it, such as qcow2.\n\t\t\t",
+        .mhandler.cmd = hmp_snapshot_blkdev_internal,
+    },
+
+STEXI
+@item snapshot_blkdev_internal
+@findex snapshot_blkdev_internal
+Take an internal snapshot on device if it support
+ETEXI
+
+    {
+        .name       = "snapshot_delete_blkdev_internal",
+        .args_type  = "device:B,name:s,id:s?",
+        .params     = "device name [id]",
+        .help       = "delete an internal snapshot of device.\n\t\t\t"
+                      "If id is specified, qemu will try delete\n\t\t\t"
+                      "the snapshot matching both id and name.\n\t\t\t"
+                      "The format of the image used by device must\n\t\t\t"
+                      "support it, such as qcow2.\n\t\t\t",
+        .mhandler.cmd = hmp_snapshot_delete_blkdev_internal,
+    },
+
+STEXI
+@item snapshot_delete_blkdev_internal
+@findex snapshot_delete_blkdev_internal
+Delete an internal snapshot on device if it support
 ETEXI
 
     {
diff --git a/hmp.c b/hmp.c
index b4a6422e7a3093712ee5969dfc83a94df78f223b..589150773e5cc86518e56255fb5b692df8fa3a30 100644 (file)
--- a/hmp.c
+++ b/hmp.c
@@ -978,6 +978,28 @@ void hmp_snapshot_blkdev(Monitor *mon, const QDict *qdict)
     hmp_handle_error(mon, &errp);
 }
 
+void hmp_snapshot_blkdev_internal(Monitor *mon, const QDict *qdict)
+{
+    const char *device = qdict_get_str(qdict, "device");
+    const char *name = qdict_get_str(qdict, "name");
+    Error *errp = NULL;
+
+    qmp_blockdev_snapshot_internal_sync(device, name, &errp);
+    hmp_handle_error(mon, &errp);
+}
+
+void hmp_snapshot_delete_blkdev_internal(Monitor *mon, const QDict *qdict)
+{
+    const char *device = qdict_get_str(qdict, "device");
+    const char *name = qdict_get_str(qdict, "name");
+    const char *id = qdict_get_try_str(qdict, "id");
+    Error *errp = NULL;
+
+    qmp_blockdev_snapshot_delete_internal_sync(device, !!id, id,
+                                               true, name, &errp);
+    hmp_handle_error(mon, &errp);
+}
+
 void hmp_migrate_cancel(Monitor *mon, const QDict *qdict)
 {
     qmp_migrate_cancel(NULL);
@@ -1141,7 +1163,7 @@ void hmp_block_stream(Monitor *mon, const QDict *qdict)
 
     qmp_block_stream(device, base != NULL, base,
                      qdict_haskey(qdict, "speed"), speed,
-                     BLOCKDEV_ON_ERROR_REPORT, true, &error);
+                     true, BLOCKDEV_ON_ERROR_REPORT, &error);
 
     hmp_handle_error(mon, &error);
 }
diff --git a/hmp.h b/hmp.h
index 6c3bdcd4c2fd6902a7eeea4012a368e485a99e7a..54cf71fb94939422c766747ab2d00b2e623e3250 100644 (file)
--- a/hmp.h
+++ b/hmp.h
@@ -54,6 +54,8 @@ void hmp_block_passwd(Monitor *mon, const QDict *qdict);
 void hmp_balloon(Monitor *mon, const QDict *qdict);
 void hmp_block_resize(Monitor *mon, const QDict *qdict);
 void hmp_snapshot_blkdev(Monitor *mon, const QDict *qdict);
+void hmp_snapshot_blkdev_internal(Monitor *mon, const QDict *qdict);
+void hmp_snapshot_delete_blkdev_internal(Monitor *mon, const QDict *qdict);
 void hmp_drive_mirror(Monitor *mon, const QDict *qdict);
 void hmp_drive_backup(Monitor *mon, const QDict *qdict);
 void hmp_migrate_cancel(Monitor *mon, const QDict *qdict);
index 668cc069ff2075291282d99610bf1c4d9b3d4d5e..f35fc5944a08a539a05adcd871e4eb155412d155 100644 (file)
@@ -809,10 +809,15 @@ static int blk_connect(struct XenDevice *xendev)
         xen_be_printf(&blkdev->xendev, 2, "create new bdrv (xenbus setup)\n");
         blkdev->bs = bdrv_new(blkdev->dev);
         if (blkdev->bs) {
+            Error *local_err = NULL;
             BlockDriver *drv = bdrv_find_whitelisted_format(blkdev->fileproto,
                                                            readonly);
             if (bdrv_open(blkdev->bs,
-                          blkdev->filename, NULL, qflags, drv) != 0) {
+                          blkdev->filename, NULL, qflags, drv, &local_err) != 0)
+            {
+                xen_be_printf(&blkdev->xendev, 0, "error: %s\n",
+                              error_get_pretty(local_err));
+                error_free(local_err);
                 bdrv_unref(blkdev->bs);
                 blkdev->bs = NULL;
             }
index 6e9fb3b69597282faf65f9f63ed76cdbb9046e21..540df826004f5629c80e34751631cf8544a85ce5 100644 (file)
@@ -31,5 +31,4 @@ obj-$(CONFIG_TCX) += tcx.o
 
 obj-$(CONFIG_VGA) += vga.o
 
-common-obj-$(CONFIG_QXL) += qxl-logger.o qxl-render.o
-obj-$(CONFIG_QXL) += qxl.o
+common-obj-$(CONFIG_QXL) += qxl.o qxl-logger.o qxl-render.o
index c50e285e2102910eec7ef7a1c3f508f130b6ca38..ee2db0da1a07c44b07b44cfd30b2517ccf570ff9 100644 (file)
@@ -313,9 +313,7 @@ static ram_addr_t qxl_rom_size(void)
                                  sizeof(qxl_modes);
     uint32_t rom_size = 8192; /* two pages */
 
-    required_rom_size = MAX(required_rom_size, TARGET_PAGE_SIZE);
-    required_rom_size = msb_mask(required_rom_size * 2 - 1);
-    assert(required_rom_size <= rom_size);
+    QEMU_BUILD_BUG_ON(required_rom_size > rom_size);
     return rom_size;
 }
 
@@ -364,7 +362,7 @@ static void init_qxl_rom(PCIQXLDevice *d)
     num_pages          = d->vga.vram_size;
     num_pages         -= ram_header_size;
     num_pages         -= surface0_area_size;
-    num_pages          = num_pages / TARGET_PAGE_SIZE;
+    num_pages          = num_pages / QXL_PAGE_SIZE;
 
     rom->draw_area_offset   = cpu_to_le32(0);
     rom->surface0_area_size = cpu_to_le32(surface0_area_size);
@@ -416,9 +414,8 @@ static void qxl_ram_set_dirty(PCIQXLDevice *qxl, void *ptr)
     intptr_t offset;
 
     offset = ptr - base;
-    offset &= ~(TARGET_PAGE_SIZE-1);
     assert(offset < qxl->vga.vram_size);
-    qxl_set_dirty(&qxl->vga.vram, offset, offset + TARGET_PAGE_SIZE);
+    qxl_set_dirty(&qxl->vga.vram, offset, offset + 3);
 }
 
 /* can be called from spice server thread context */
@@ -528,7 +525,8 @@ static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info)
     info->num_memslots = NUM_MEMSLOTS;
     info->num_memslots_groups = NUM_MEMSLOTS_GROUPS;
     info->internal_groupslot_id = 0;
-    info->qxl_ram_size = le32_to_cpu(qxl->shadow_rom.num_pages) << TARGET_PAGE_BITS;
+    info->qxl_ram_size =
+        le32_to_cpu(qxl->shadow_rom.num_pages) << QXL_PAGE_BITS;
     info->n_surfaces = qxl->ssd.num_surfaces;
 }
 
index 8e9b0c299efc33b4d589121249812f1221d488e4..84f01823833c810a016e6739e588a77cae290b46 100644 (file)
@@ -27,6 +27,9 @@ enum qxl_mode {
 
 #define QXL_NUM_DIRTY_RECTS 64
 
+#define QXL_PAGE_BITS 12
+#define QXL_PAGE_SIZE (1 << QXL_PAGE_BITS);
+
 typedef struct PCIQXLDevice {
     PCIDevice          pci;
     SimpleSpiceDisplay ssd;
index 8246a1bdd499fea325541bd760aafcf02f2e7530..e917c83540fc3e9adbfbee8648ccca6d550d6823 100644 (file)
@@ -53,10 +53,7 @@ static void pc_isa_bios_init(MemoryRegion *rom_memory,
     flash_size = memory_region_size(flash_mem);
 
     /* map the last 128KB of the BIOS in ISA space */
-    isa_bios_size = flash_size;
-    if (isa_bios_size > (128 * 1024)) {
-        isa_bios_size = 128 * 1024;
-    }
+    isa_bios_size = MIN(flash_size, 128 * 1024);
     isa_bios = g_malloc(sizeof(*isa_bios));
     memory_region_init_ram(isa_bios, NULL, "isa-bios", isa_bios_size);
     vmstate_register_ram_global(isa_bios);
index 0c368427292c94f9c3f191e78049bb3aa48ec9f9..36e5f5036014807b4c8602aae437f7aac8dbb1e2 100644 (file)
@@ -7,8 +7,11 @@
  * This code is licensed under the LGPL.
  */
 
-/* ??? Need to check if the {read,write}[wl] routines work properly on
-   big-endian targets.  */
+/* Note:
+ * LSI53C810 emulation is incorrect, in the sense that it supports
+ * features added in later evolutions. This should not be a problem,
+ * as well-behaved operating systems will not try to use them.
+ */
 
 #include <assert.h>
 
@@ -278,6 +281,7 @@ typedef struct {
     uint32_t script_ram[2048];
 } LSIState;
 
+#define TYPE_LSI53C810  "lsi53c810"
 #define TYPE_LSI53C895A "lsi53c895a"
 
 #define LSI53C895A(obj) \
@@ -998,12 +1002,6 @@ bad:
     s->msg_action = 0;
 }
 
-/* Sign extend a 24-bit value.  */
-static inline int32_t sxt24(int32_t n)
-{
-    return (n << 8) >> 8;
-}
-
 #define LSI_BUF_SIZE 4096
 static void lsi_memcpy(LSIState *s, uint32_t dest, uint32_t src, int count)
 {
@@ -1083,7 +1081,7 @@ again:
             /* Table indirect addressing.  */
 
             /* 32-bit Table indirect */
-            offset = sxt24(addr);
+            offset = sextract32(addr, 0, 24);
             pci_dma_read(pci_dev, s->dsa + offset, buf, 8);
             /* byte count is stored in bits 0:23 only */
             s->dbc = cpu_to_le32(buf[0]) & 0xffffff;
@@ -1183,13 +1181,13 @@ again:
             uint32_t id;
 
             if (insn & (1 << 25)) {
-                id = read_dword(s, s->dsa + sxt24(insn));
+                id = read_dword(s, s->dsa + sextract32(insn, 0, 24));
             } else {
                 id = insn;
             }
             id = (id >> 16) & 0xf;
             if (insn & (1 << 26)) {
-                addr = s->dsp + sxt24(addr);
+                addr = s->dsp + sextract32(addr, 0, 24);
             }
             s->dnad = addr;
             switch (opcode) {
@@ -1385,7 +1383,7 @@ again:
             if (cond == jmp) {
                 if (insn & (1 << 23)) {
                     /* Relative address.  */
-                    addr = s->dsp + sxt24(addr);
+                    addr = s->dsp + sextract32(addr, 0, 24);
                 }
                 switch ((insn >> 27) & 7) {
                 case 0: /* Jump */
@@ -1438,7 +1436,7 @@ again:
             int i;
 
             if (insn & (1 << 28)) {
-                addr = s->dsa + sxt24(addr);
+                addr = s->dsa + sextract32(addr, 0, 24);
             }
             n = (insn & 7);
             reg = (insn >> 16) & 0xff;
@@ -1521,7 +1519,7 @@ static uint8_t lsi_reg_readb(LSIState *s, int offset)
            used for diagnostics, so should be ok.  */
         return 0;
     case 0xc: /* DSTAT */
-        tmp = s->dstat | 0x80;
+        tmp = s->dstat | LSI_DSTAT_DFE;
         if ((s->istat0 & LSI_ISTAT0_INTF) == 0)
             s->dstat = 0;
         lsi_update_irq(s);
@@ -1705,8 +1703,9 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val)
         s->sxfer = val;
         break;
     case 0x06: /* SDID */
-        if ((val & 0xf) != (s->ssid & 0xf))
+        if ((s->ssid & 0x80) && (val & 0xf) != (s->ssid & 0xf)) {
             BADF("Destination ID does not match SSID\n");
+        }
         s->sdid = val & 0xf;
         break;
     case 0x07: /* GPREG0 */
@@ -1748,6 +1747,9 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val)
     case 0x17: /* MBOX1 */
         s->mbox1 = val;
         break;
+    case 0x18: /* CTEST0 */
+        /* nothing to do */
+        break;
     case 0x1a: /* CTEST2 */
        s->ctest2 = val & LSI_CTEST2_PCICIE;
        break;
@@ -1876,8 +1878,7 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val)
             int shift;
             n = (offset - 0x58) >> 2;
             shift = (offset & 3) * 8;
-            s->scratch[n] &= ~(0xff << shift);
-            s->scratch[n] |= (val & 0xff) << shift;
+            s->scratch[n] = deposit32(s->scratch[n], shift, 8, val);
         } else {
             BADF("Unhandled writeb 0x%x = 0x%x\n", offset, val);
         }
@@ -2113,7 +2114,7 @@ static int lsi_scsi_init(PCIDevice *dev)
                           "lsi-io", 256);
 
     pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->io_io);
-    pci_register_bar(dev, 1, 0, &s->mmio_io);
+    pci_register_bar(dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio_io);
     pci_register_bar(dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->ram_io);
     QTAILQ_INIT(&s->queue);
 
@@ -2151,9 +2152,23 @@ static const TypeInfo lsi_info = {
     .class_init    = lsi_class_init,
 };
 
+static void lsi53c810_class_init(ObjectClass *klass, void *data)
+{
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->device_id = PCI_DEVICE_ID_LSI_53C810;
+}
+
+static TypeInfo lsi53c810_info = {
+    .name          = TYPE_LSI53C810,
+    .parent        = TYPE_LSI53C895A,
+    .class_init    = lsi53c810_class_init,
+};
+
 static void lsi53c895a_register_types(void)
 {
     type_register_static(&lsi_info);
+    type_register_static(&lsi53c810_info);
 }
 
 type_init(lsi53c895a_register_types)
index 5cd6137046138e51241e6cc17e12879a7746d6a3..4d36841d403769d018c06815d757e0ff07ff2f39 100644 (file)
@@ -224,7 +224,7 @@ SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv,
     if (object_property_find(OBJECT(dev), "removable", NULL)) {
         qdev_prop_set_bit(dev, "removable", removable);
     }
-    if (serial) {
+    if (serial && object_property_find(OBJECT(dev), "serial", NULL)) {
         qdev_prop_set_string(dev, "serial", serial);
     }
     if (qdev_prop_set_drive(dev, "drive", bdrv) < 0) {
index b2fcd4b3e3299104250c086edb01157eb25415da..2a26042701a17a3724ec4aeb72daaec3c38d0064 100644 (file)
@@ -117,6 +117,20 @@ static struct vscsi_req *vscsi_get_req(VSCSIState *s)
     return NULL;
 }
 
+static struct vscsi_req *vscsi_find_req(VSCSIState *s, uint64_t srp_tag)
+{
+    vscsi_req *req;
+    int i;
+
+    for (i = 0; i < VSCSI_REQ_LIMIT; i++) {
+        req = &s->reqs[i];
+        if (req->iu.srp.cmd.tag == srp_tag) {
+            return req;
+        }
+    }
+    return NULL;
+}
+
 static void vscsi_put_req(vscsi_req *req)
 {
     if (req->sreq != NULL) {
@@ -755,40 +769,91 @@ static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req)
 static int vscsi_process_tsk_mgmt(VSCSIState *s, vscsi_req *req)
 {
     union viosrp_iu *iu = &req->iu;
-    int fn;
+    vscsi_req *tmpreq;
+    int i, lun = 0, resp = SRP_TSK_MGMT_COMPLETE;
+    SCSIDevice *d;
+    uint64_t tag = iu->srp.rsp.tag;
+    uint8_t sol_not = iu->srp.cmd.sol_not;
 
     fprintf(stderr, "vscsi_process_tsk_mgmt %02x\n",
             iu->srp.tsk_mgmt.tsk_mgmt_func);
 
-    switch (iu->srp.tsk_mgmt.tsk_mgmt_func) {
-#if 0 /* We really don't deal with these for now */
-    case SRP_TSK_ABORT_TASK:
-        fn = ABORT_TASK;
-        break;
-    case SRP_TSK_ABORT_TASK_SET:
-        fn = ABORT_TASK_SET;
-        break;
-    case SRP_TSK_CLEAR_TASK_SET:
-        fn = CLEAR_TASK_SET;
-        break;
-    case SRP_TSK_LUN_RESET:
-        fn = LOGICAL_UNIT_RESET;
-        break;
-    case SRP_TSK_CLEAR_ACA:
-        fn = CLEAR_ACA;
-        break;
-#endif
-    default:
-        fn = 0;
+    d = vscsi_device_find(&s->bus, be64_to_cpu(req->iu.srp.tsk_mgmt.lun), &lun);
+    if (!d) {
+        resp = SRP_TSK_MGMT_FIELDS_INVALID;
+    } else {
+        switch (iu->srp.tsk_mgmt.tsk_mgmt_func) {
+        case SRP_TSK_ABORT_TASK:
+            if (d->lun != lun) {
+                resp = SRP_TSK_MGMT_FIELDS_INVALID;
+                break;
+            }
+
+            tmpreq = vscsi_find_req(s, req->iu.srp.tsk_mgmt.task_tag);
+            if (tmpreq && tmpreq->sreq) {
+                assert(tmpreq->sreq->hba_private);
+                scsi_req_cancel(tmpreq->sreq);
+            }
+            break;
+
+        case SRP_TSK_LUN_RESET:
+            if (d->lun != lun) {
+                resp = SRP_TSK_MGMT_FIELDS_INVALID;
+                break;
+            }
+
+            qdev_reset_all(&d->qdev);
+            break;
+
+        case SRP_TSK_ABORT_TASK_SET:
+        case SRP_TSK_CLEAR_TASK_SET:
+            if (d->lun != lun) {
+                resp = SRP_TSK_MGMT_FIELDS_INVALID;
+                break;
+            }
+
+            for (i = 0; i < VSCSI_REQ_LIMIT; i++) {
+                tmpreq = &s->reqs[i];
+                if (tmpreq->iu.srp.cmd.lun != req->iu.srp.tsk_mgmt.lun) {
+                    continue;
+                }
+                if (!tmpreq->active || !tmpreq->sreq) {
+                    continue;
+                }
+                assert(tmpreq->sreq->hba_private);
+                scsi_req_cancel(tmpreq->sreq);
+            }
+            break;
+
+        case SRP_TSK_CLEAR_ACA:
+            resp = SRP_TSK_MGMT_NOT_SUPPORTED;
+            break;
+
+        default:
+            resp = SRP_TSK_MGMT_FIELDS_INVALID;
+            break;
+        }
     }
-    if (fn) {
-        /* XXX Send/Handle target task management */
-        ;
+
+    /* Compose the response here as  */
+    memset(iu, 0, sizeof(struct srp_rsp) + 4);
+    iu->srp.rsp.opcode = SRP_RSP;
+    iu->srp.rsp.req_lim_delta = cpu_to_be32(1);
+    iu->srp.rsp.tag = tag;
+    iu->srp.rsp.flags |= SRP_RSP_FLAG_RSPVALID;
+    iu->srp.rsp.resp_data_len = cpu_to_be32(4);
+    if (resp) {
+        iu->srp.rsp.sol_not = (sol_not & 0x04) >> 2;
     } else {
-        vscsi_makeup_sense(s, req, ILLEGAL_REQUEST, 0x20, 0);
-        vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0);
+        iu->srp.rsp.sol_not = (sol_not & 0x02) >> 1;
     }
-    return !fn;
+
+    iu->srp.rsp.status = GOOD;
+    iu->srp.rsp.data[3] = resp;
+
+    vscsi_send_iu(s, req, sizeof(iu->srp.rsp) + 4, VIOSRP_SRP_FORMAT);
+
+    return 1;
 }
 
 static int vscsi_handle_srp_req(VSCSIState *s, vscsi_req *req)
@@ -858,29 +923,97 @@ static int vscsi_send_adapter_info(VSCSIState *s, vscsi_req *req)
     return vscsi_send_iu(s, req, sizeof(*sinfo), VIOSRP_MAD_FORMAT);
 }
 
+static int vscsi_send_capabilities(VSCSIState *s, vscsi_req *req)
+{
+    struct viosrp_capabilities *vcap;
+    struct capabilities cap = { };
+    uint16_t len, req_len;
+    uint64_t buffer;
+    int rc;
+
+    vcap = &req->iu.mad.capabilities;
+    req_len = len = be16_to_cpu(vcap->common.length);
+    buffer = be64_to_cpu(vcap->buffer);
+    if (len > sizeof(cap)) {
+        fprintf(stderr, "vscsi_send_capabilities: capabilities size mismatch !\n");
+
+        /*
+         * Just read and populate the structure that is known.
+         * Zero rest of the structure.
+         */
+        len = sizeof(cap);
+    }
+    rc = spapr_vio_dma_read(&s->vdev, buffer, &cap, len);
+    if (rc)  {
+        fprintf(stderr, "vscsi_send_capabilities: DMA read failure !\n");
+    }
+
+    /*
+     * Current implementation does not suppport any migration or
+     * reservation capabilities. Construct the response telling the
+     * guest not to use them.
+     */
+    cap.flags = 0;
+    cap.migration.ecl = 0;
+    cap.reserve.type = 0;
+    cap.migration.common.server_support = 0;
+    cap.reserve.common.server_support = 0;
+
+    rc = spapr_vio_dma_write(&s->vdev, buffer, &cap, len);
+    if (rc)  {
+        fprintf(stderr, "vscsi_send_capabilities: DMA write failure !\n");
+    }
+    if (req_len > len) {
+        /*
+         * Being paranoid and lets not worry about the error code
+         * here. Actual write of the cap is done above.
+         */
+        spapr_vio_dma_set(&s->vdev, (buffer + len), 0, (req_len - len));
+    }
+    vcap->common.status = rc ? cpu_to_be32(1) : 0;
+    return vscsi_send_iu(s, req, sizeof(*vcap), VIOSRP_MAD_FORMAT);
+}
+
 static int vscsi_handle_mad_req(VSCSIState *s, vscsi_req *req)
 {
     union mad_iu *mad = &req->iu.mad;
+    bool request_handled = false;
+    uint64_t retlen = 0;
 
     switch (be32_to_cpu(mad->empty_iu.common.type)) {
     case VIOSRP_EMPTY_IU_TYPE:
         fprintf(stderr, "Unsupported EMPTY MAD IU\n");
+        retlen = sizeof(mad->empty_iu);
         break;
     case VIOSRP_ERROR_LOG_TYPE:
         fprintf(stderr, "Unsupported ERROR LOG MAD IU\n");
-        mad->error_log.common.status = cpu_to_be16(1);
-        vscsi_send_iu(s, req, sizeof(mad->error_log), VIOSRP_MAD_FORMAT);
+        retlen = sizeof(mad->error_log);
         break;
     case VIOSRP_ADAPTER_INFO_TYPE:
         vscsi_send_adapter_info(s, req);
+        request_handled = true;
         break;
     case VIOSRP_HOST_CONFIG_TYPE:
-        mad->host_config.common.status = cpu_to_be16(1);
-        vscsi_send_iu(s, req, sizeof(mad->host_config), VIOSRP_MAD_FORMAT);
+        retlen = sizeof(mad->host_config);
+        break;
+    case VIOSRP_CAPABILITIES_TYPE:
+        vscsi_send_capabilities(s, req);
+        request_handled = true;
         break;
     default:
         fprintf(stderr, "VSCSI: Unknown MAD type %02x\n",
                 be32_to_cpu(mad->empty_iu.common.type));
+        /*
+         * PAPR+ says that "The length field is set to the length
+         * of the data structure(s) used in the command".
+         * As we did not recognize the request type, put zero there.
+         */
+        retlen = 0;
+    }
+
+    if (!request_handled) {
+        mad->empty_iu.common.status = cpu_to_be16(VIOSRP_MAD_NOT_SUPPORTED);
+        vscsi_send_iu(s, req, retlen, VIOSRP_MAD_FORMAT);
     }
 
     return 1;
index 5e0cad5c196fcd125b95d38c4ec3daf4e6e404c8..d27f31d2d530364ba8f4a9d35481aad99356c7a5 100644 (file)
@@ -90,6 +90,13 @@ enum {
     SRP_REV16A_IB_IO_CLASS = 0x0100
 };
 
+enum {
+    SRP_TSK_MGMT_COMPLETE       = 0x00,
+    SRP_TSK_MGMT_FIELDS_INVALID = 0x02,
+    SRP_TSK_MGMT_NOT_SUPPORTED  = 0x04,
+    SRP_TSK_MGMT_FAILED         = 0x05
+};
+
 struct srp_direct_buf {
     uint64_t    va;
     uint32_t    key;
index 3bd690d128239c6f7d60954d022591bb94a69bfa..26d95a14ec28bc82a3f3d092413afb1b30ac3a93 100644 (file)
@@ -693,6 +693,7 @@ static const TypeInfo virtio_scsi_common_info = {
     .name = TYPE_VIRTIO_SCSI_COMMON,
     .parent = TYPE_VIRTIO_DEVICE,
     .instance_size = sizeof(VirtIOSCSICommon),
+    .abstract = true,
     .class_init = virtio_scsi_common_class_init,
 };
 
index 13f6602ad242e2643fb9518869b728d3a16066ee..ad77705f8cec1d45de4e8e11ceaec2c45a22bd7a 100644 (file)
@@ -39,6 +39,7 @@ static void usb_combined_packet_remove(USBCombinedPacket *combined,
     p->combined = NULL;
     QTAILQ_REMOVE(&combined->packets, p, combined_entry);
     if (QTAILQ_EMPTY(&combined->packets)) {
+        qemu_iovec_destroy(&combined->iov);
         g_free(combined);
     }
 }
index 31960c28a84df12ac50bbe3e72f0e626b61bda99..cf59a1abcf2ac2aa3c9b371bd3d9af1727dc3470 100644 (file)
@@ -622,6 +622,7 @@ void usb_ep_reset(USBDevice *dev)
     dev->ep_ctl.nr = 0;
     dev->ep_ctl.type = USB_ENDPOINT_XFER_CONTROL;
     dev->ep_ctl.ifnum = 0;
+    dev->ep_ctl.max_packet_size = 64;
     dev->ep_ctl.dev = dev;
     dev->ep_ctl.pipeline = false;
     for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) {
@@ -633,6 +634,8 @@ void usb_ep_reset(USBDevice *dev)
         dev->ep_out[ep].type = USB_ENDPOINT_XFER_INVALID;
         dev->ep_in[ep].ifnum = USB_INTERFACE_INVALID;
         dev->ep_out[ep].ifnum = USB_INTERFACE_INVALID;
+        dev->ep_in[ep].max_packet_size = 0;
+        dev->ep_out[ep].max_packet_size = 0;
         dev->ep_in[ep].dev = dev;
         dev->ep_out[ep].dev = dev;
         dev->ep_in[ep].pipeline = false;
index f02231dc87eee81903253459746b67c157dce7e0..469c24d768523f94854efadf4ecc38b76a276e85 100644 (file)
@@ -509,6 +509,8 @@ static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid,
                                 unsigned int epid);
 static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v);
 static void xhci_write_event(XHCIState *xhci, XHCIEvent *event, int v);
+static USBEndpoint *xhci_epid_to_usbep(XHCIState *xhci,
+                                       unsigned int slotid, unsigned int epid);
 
 static const char *TRBType_names[] = {
     [TRB_RESERVED]                     = "TRB_RESERVED",
@@ -1138,7 +1140,7 @@ static void xhci_reset_streams(XHCIEPContext *epctx)
 static void xhci_alloc_streams(XHCIEPContext *epctx, dma_addr_t base)
 {
     assert(epctx->pstreams == NULL);
-    epctx->nr_pstreams = 2 << epctx->max_pstreams;
+    epctx->nr_pstreams = 2 << (epctx->max_pstreams + 1);
     epctx->pstreams = xhci_alloc_stream_contexts(epctx->nr_pstreams, base);
 }
 
@@ -1245,6 +1247,9 @@ static XHCIEPContext *xhci_alloc_epctx(XHCIState *xhci,
     epctx->epid = epid;
 
     for (i = 0; i < ARRAY_SIZE(epctx->transfers); i++) {
+        epctx->transfers[i].xhci = xhci;
+        epctx->transfers[i].slotid = slotid;
+        epctx->transfers[i].epid = epid;
         usb_packet_init(&epctx->transfers[i].packet);
     }
     epctx->kick_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, xhci_ep_kick_timer, epctx);
@@ -1358,13 +1363,12 @@ static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid,
 
     xferi = epctx->next_xfer;
     for (i = 0; i < TD_QUEUE; i++) {
-        if (epctx->transfers[xferi].packet.ep) {
-            ep = epctx->transfers[xferi].packet.ep;
-        }
         killed += xhci_ep_nuke_one_xfer(&epctx->transfers[xferi]);
         epctx->transfers[xferi].packet.ep = NULL;
         xferi = (xferi + 1) % TD_QUEUE;
     }
+
+    ep = xhci_epid_to_usbep(xhci, slotid, epid);
     if (ep) {
         usb_device_ep_stopped(ep->dev, ep);
     }
@@ -1376,6 +1380,7 @@ static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid,
 {
     XHCISlot *slot;
     XHCIEPContext *epctx;
+    int i;
 
     trace_usb_xhci_ep_disable(slotid, epid);
     assert(slotid >= 1 && slotid <= xhci->numslots);
@@ -1396,6 +1401,10 @@ static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid,
         xhci_free_streams(epctx);
     }
 
+    for (i = 0; i < ARRAY_SIZE(epctx->transfers); i++) {
+        usb_packet_cleanup(&epctx->transfers[i].packet);
+    }
+
     xhci_set_ep_state(xhci, epctx, NULL, EP_DISABLED);
 
     timer_free(epctx->kick_timer);
@@ -1696,7 +1705,6 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer,
 static int xhci_setup_packet(XHCITransfer *xfer)
 {
     XHCIState *xhci = xfer->xhci;
-    USBDevice *dev;
     USBEndpoint *ep;
     int dir;
 
@@ -1704,15 +1712,13 @@ static int xhci_setup_packet(XHCITransfer *xfer)
 
     if (xfer->packet.ep) {
         ep = xfer->packet.ep;
-        dev = ep->dev;
     } else {
-        if (!xhci->slots[xfer->slotid-1].uport) {
+        ep = xhci_epid_to_usbep(xhci, xfer->slotid, xfer->epid);
+        if (!ep) {
             fprintf(stderr, "xhci: slot %d has no device\n",
                     xfer->slotid);
             return -1;
         }
-        dev = xhci->slots[xfer->slotid-1].uport->dev;
-        ep = usb_ep_get(dev, dir, xfer->epid >> 1);
     }
 
     xhci_xfer_create_sgl(xfer, dir == USB_TOKEN_IN); /* Also sets int_req */
@@ -1720,7 +1726,7 @@ static int xhci_setup_packet(XHCITransfer *xfer)
                      xfer->trbs[0].addr, false, xfer->int_req);
     usb_packet_map(&xfer->packet, &xfer->sgl);
     DPRINTF("xhci: setup packet pid 0x%x addr %d ep %d\n",
-            xfer->packet.pid, dev->addr, ep->nr);
+            xfer->packet.pid, ep->dev->addr, ep->nr);
     return 0;
 }
 
@@ -2060,9 +2066,6 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid,
         for (i = 0; i < length; i++) {
             assert(xhci_ring_fetch(xhci, ring, &xfer->trbs[i], NULL));
         }
-        xfer->xhci = xhci;
-        xfer->epid = epid;
-        xfer->slotid = slotid;
         xfer->streamid = streamid;
 
         if (epid == 1) {
@@ -2075,7 +2078,6 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid,
         } else {
             if (xhci_fire_transfer(xhci, xfer, epctx) >= 0) {
                 epctx->next_xfer = (epctx->next_xfer + 1) % TD_QUEUE;
-                ep = xfer->packet.ep;
             } else {
                 if (!xfer->timed_xfer) {
                     fprintf(stderr, "xhci: error firing data transfer\n");
@@ -2092,6 +2094,8 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid,
             break;
         }
     }
+
+    ep = xhci_epid_to_usbep(xhci, slotid, epid);
     if (ep) {
         usb_device_flush_ep_queue(ep->dev, ep);
     }
@@ -3321,6 +3325,19 @@ static int xhci_find_epid(USBEndpoint *ep)
     }
 }
 
+static USBEndpoint *xhci_epid_to_usbep(XHCIState *xhci,
+                                       unsigned int slotid, unsigned int epid)
+{
+    assert(slotid >= 1 && slotid <= xhci->numslots);
+
+    if (!xhci->slots[slotid - 1].uport) {
+        return NULL;
+    }
+
+    return usb_ep_get(xhci->slots[slotid - 1].uport->dev,
+                      (epid & 1) ? USB_TOKEN_IN : USB_TOKEN_OUT, epid >> 1);
+}
+
 static void xhci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep,
                                  unsigned int stream)
 {
diff --git a/hw/usb/host-bsd.c b/hw/usb/host-bsd.c
deleted file mode 100644 (file)
index 39f2281..0000000
+++ /dev/null
@@ -1,639 +0,0 @@
-/*
- * BSD host USB redirector
- *
- * Copyright (c) 2006 Lonnie Mendez
- * Portions of code and concepts borrowed from
- * usb-linux.c and libusb's bsd.c and are copyright their respective owners.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "qemu-common.h"
-#include "monitor/monitor.h"
-#include "hw/usb.h"
-
-/* usb.h declares these */
-#undef USB_SPEED_HIGH
-#undef USB_SPEED_FULL
-#undef USB_SPEED_LOW
-
-#include <sys/ioctl.h>
-#ifndef __DragonFly__
-#include <dev/usb/usb.h>
-#else
-#include <bus/usb/usb.h>
-#endif
-
-/* This value has maximum potential at 16.
- * You should also set hw.usb.debug to gain
- * more detailed view.
- */
-//#define DEBUG
-#define UGEN_DEBUG_LEVEL 0
-
-
-typedef int USBScanFunc(void *opaque, int bus_num, int addr, int class_id,
-                        int vendor_id, int product_id,
-                        const char *product_name, int speed);
-static int usb_host_find_device(int *pbus_num, int *paddr,
-                                const char *devname);
-
-typedef struct USBHostDevice {
-    USBDevice dev;
-    int ep_fd[USB_MAX_ENDPOINTS];
-    int devfd;
-    char devpath[32];
-} USBHostDevice;
-
-
-static int ensure_ep_open(USBHostDevice *dev, int ep, int mode)
-{
-    char buf[32];
-    int fd;
-
-    /* Get the address for this endpoint */
-    ep = UE_GET_ADDR(ep);
-
-    if (dev->ep_fd[ep] < 0) {
-#if defined(__FreeBSD__) || defined(__DragonFly__)
-        snprintf(buf, sizeof(buf) - 1, "%s.%d", dev->devpath, ep);
-#else
-        snprintf(buf, sizeof(buf) - 1, "%s.%02d", dev->devpath, ep);
-#endif
-        /* Try to open it O_RDWR first for those devices which have in and out
-         * endpoints with the same address (eg 0x02 and 0x82)
-         */
-        fd = open(buf, O_RDWR);
-        if (fd < 0 && errno == ENXIO)
-            fd = open(buf, mode);
-        if (fd < 0) {
-#ifdef DEBUG
-            printf("ensure_ep_open: failed to open device endpoint %s: %s\n",
-                   buf, strerror(errno));
-#endif
-        }
-        dev->ep_fd[ep] = fd;
-    }
-
-    return dev->ep_fd[ep];
-}
-
-static void ensure_eps_closed(USBHostDevice *dev)
-{
-    int epnum = 1;
-
-    if (!dev)
-        return;
-
-    while (epnum < USB_MAX_ENDPOINTS) {
-        if (dev->ep_fd[epnum] >= 0) {
-            close(dev->ep_fd[epnum]);
-            dev->ep_fd[epnum] = -1;
-        }
-        epnum++;
-    }
-}
-
-static void usb_host_handle_reset(USBDevice *dev)
-{
-#if 0
-    USBHostDevice *s = (USBHostDevice *)dev;
-#endif
-}
-
-/* XXX:
- * -check device states against transfer requests
- *  and return appropriate response
- */
-static void usb_host_handle_control(USBDevice *dev,
-                                   USBPacket *p,
-                                   int request,
-                                   int value,
-                                   int index,
-                                   int length,
-                                   uint8_t *data)
-{
-    USBHostDevice *s = (USBHostDevice *)dev;
-    struct usb_ctl_request req;
-    struct usb_alt_interface aiface;
-    int ret, timeout = 50;
-
-    if ((request >> 8) == UT_WRITE_DEVICE &&
-        (request & 0xff) == UR_SET_ADDRESS) {
-
-        /* specific SET_ADDRESS support */
-        dev->addr = value;
-    } else if ((request >> 8) == UT_WRITE_DEVICE &&
-               (request & 0xff) == UR_SET_CONFIG) {
-
-        ensure_eps_closed(s); /* can't do this without all eps closed */
-
-        ret = ioctl(s->devfd, USB_SET_CONFIG, &value);
-        if (ret < 0) {
-#ifdef DEBUG
-            printf("handle_control: failed to set configuration - %s\n",
-                   strerror(errno));
-#endif
-            p->status = USB_RET_STALL;
-        }
-    } else if ((request >> 8) == UT_WRITE_INTERFACE &&
-               (request & 0xff) == UR_SET_INTERFACE) {
-
-        aiface.uai_interface_index = index;
-        aiface.uai_alt_no = value;
-
-        ensure_eps_closed(s); /* can't do this without all eps closed */
-        ret = ioctl(s->devfd, USB_SET_ALTINTERFACE, &aiface);
-        if (ret < 0) {
-#ifdef DEBUG
-            printf("handle_control: failed to set alternate interface - %s\n",
-                   strerror(errno));
-#endif
-            p->status = USB_RET_STALL;
-        }
-    } else {
-        req.ucr_request.bmRequestType = request >> 8;
-        req.ucr_request.bRequest = request & 0xff;
-        USETW(req.ucr_request.wValue, value);
-        USETW(req.ucr_request.wIndex, index);
-        USETW(req.ucr_request.wLength, length);
-        req.ucr_data = data;
-        req.ucr_flags = USBD_SHORT_XFER_OK;
-
-        ret = ioctl(s->devfd, USB_SET_TIMEOUT, &timeout);
-#if defined(__NetBSD__) || defined(__OpenBSD__)
-        if (ret < 0 && errno != EINVAL) {
-#else
-        if (ret < 0) {
-#endif
-#ifdef DEBUG
-            printf("handle_control: setting timeout failed - %s\n",
-                   strerror(errno));
-#endif
-        }
-
-        ret = ioctl(s->devfd, USB_DO_REQUEST, &req);
-        /* ugen returns EIO for usbd_do_request_ no matter what
-         * happens with the transfer */
-        if (ret < 0) {
-#ifdef DEBUG
-            printf("handle_control: error after request - %s\n",
-                   strerror(errno));
-#endif
-            p->status = USB_RET_NAK; /* STALL */
-        } else {
-            p->actual_length = req.ucr_actlen;
-        }
-    }
-}
-
-static void usb_host_handle_data(USBDevice *dev, USBPacket *p)
-{
-    USBHostDevice *s = (USBHostDevice *)dev;
-    int ret, fd, mode;
-    int one = 1, shortpacket = 0, timeout = 50;
-    sigset_t new_mask, old_mask;
-    uint8_t devep = p->ep->nr;
-
-    /* protect data transfers from SIGALRM signal */
-    sigemptyset(&new_mask);
-    sigaddset(&new_mask, SIGALRM);
-    sigprocmask(SIG_BLOCK, &new_mask, &old_mask);
-
-    if (p->pid == USB_TOKEN_IN) {
-        devep |= 0x80;
-        mode = O_RDONLY;
-        shortpacket = 1;
-    } else {
-        mode = O_WRONLY;
-    }
-
-    fd = ensure_ep_open(s, devep, mode);
-    if (fd < 0) {
-        sigprocmask(SIG_SETMASK, &old_mask, NULL);
-        p->status = USB_RET_NODEV;
-        return;
-    }
-
-    if (ioctl(fd, USB_SET_TIMEOUT, &timeout) < 0) {
-#ifdef DEBUG
-        printf("handle_data: failed to set timeout - %s\n",
-               strerror(errno));
-#endif
-    }
-
-    if (shortpacket) {
-        if (ioctl(fd, USB_SET_SHORT_XFER, &one) < 0) {
-#ifdef DEBUG
-            printf("handle_data: failed to set short xfer mode - %s\n",
-                   strerror(errno));
-#endif
-            sigprocmask(SIG_SETMASK, &old_mask, NULL);
-        }
-    }
-
-    if (p->pid == USB_TOKEN_IN)
-        ret = readv(fd, p->iov.iov, p->iov.niov);
-    else
-        ret = writev(fd, p->iov.iov, p->iov.niov);
-
-    sigprocmask(SIG_SETMASK, &old_mask, NULL);
-
-    if (ret < 0) {
-#ifdef DEBUG
-        printf("handle_data: error after %s data - %s\n",
-               pid == USB_TOKEN_IN ? "reading" : "writing", strerror(errno));
-#endif
-        switch(errno) {
-        case ETIMEDOUT:
-        case EINTR:
-            p->status = USB_RET_NAK;
-            break;
-        default:
-            p->status = USB_RET_STALL;
-        }
-    } else {
-        p->actual_length = ret;
-    }
-}
-
-static void usb_host_handle_destroy(USBDevice *opaque)
-{
-    USBHostDevice *s = (USBHostDevice *)opaque;
-    int i;
-
-    for (i = 0; i < USB_MAX_ENDPOINTS; i++)
-        if (s->ep_fd[i] >= 0)
-            close(s->ep_fd[i]);
-
-    if (s->devfd < 0)
-        return;
-
-    close(s->devfd);
-
-    g_free(s);
-}
-
-static int usb_host_initfn(USBDevice *dev)
-{
-    dev->flags |= (1 << USB_DEV_FLAG_IS_HOST);
-    return 0;
-}
-
-USBDevice *usb_host_device_open(USBBus *guest_bus, const char *devname)
-{
-    struct usb_device_info bus_info, dev_info;
-    USBDevice *d = NULL, *ret = NULL;
-    USBHostDevice *dev;
-    char ctlpath[PATH_MAX + 1];
-    char buspath[PATH_MAX + 1];
-    int bfd, dfd, bus, address, i;
-    int ugendebug = UGEN_DEBUG_LEVEL;
-
-    if (usb_host_find_device(&bus, &address, devname) < 0) {
-        goto fail;
-    }
-
-    snprintf(buspath, PATH_MAX, "/dev/usb%d", bus);
-
-    bfd = open(buspath, O_RDWR);
-    if (bfd < 0) {
-#ifdef DEBUG
-        printf("usb_host_device_open: failed to open usb bus - %s\n",
-               strerror(errno));
-#endif
-        goto fail;
-    }
-
-    bus_info.udi_addr = address;
-    if (ioctl(bfd, USB_DEVICEINFO, &bus_info) < 0) {
-#ifdef DEBUG
-        printf("usb_host_device_open: failed to grab bus information - %s\n",
-               strerror(errno));
-#endif
-        goto fail_bfd;
-    }
-
-#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
-    snprintf(ctlpath, PATH_MAX, "/dev/%s", bus_info.udi_devnames[0]);
-#else
-    snprintf(ctlpath, PATH_MAX, "/dev/%s.00", bus_info.udi_devnames[0]);
-#endif
-
-    dfd  = open(ctlpath, O_RDWR);
-    if (dfd < 0) {
-        dfd = open(ctlpath, O_RDONLY);
-        if (dfd < 0) {
-#ifdef DEBUG
-            printf("usb_host_device_open: failed to open usb device %s - %s\n",
-                   ctlpath, strerror(errno));
-#endif
-        }
-        goto fail_dfd;
-    }
-
-    if (ioctl(dfd, USB_GET_DEVICEINFO, &dev_info) < 0) {
-#ifdef DEBUG
-        printf("usb_host_device_open: failed to grab device info - %s\n",
-               strerror(errno));
-#endif
-        goto fail_dfd;
-    }
-
-    d = usb_create(guest_bus, "usb-host");
-    dev = DO_UPCAST(USBHostDevice, dev, d);
-
-    if (dev_info.udi_speed == 1) {
-        dev->dev.speed = USB_SPEED_LOW - 1;
-        dev->dev.speedmask = USB_SPEED_MASK_LOW;
-    } else {
-        dev->dev.speed = USB_SPEED_FULL - 1;
-        dev->dev.speedmask = USB_SPEED_MASK_FULL;
-    }
-
-    if (strncmp(dev_info.udi_product, "product", 7) != 0) {
-        pstrcpy(dev->dev.product_desc, sizeof(dev->dev.product_desc),
-                dev_info.udi_product);
-    } else {
-        snprintf(dev->dev.product_desc, sizeof(dev->dev.product_desc),
-                 "host:%s", devname);
-    }
-
-    pstrcpy(dev->devpath, sizeof(dev->devpath), "/dev/");
-    pstrcat(dev->devpath, sizeof(dev->devpath), dev_info.udi_devnames[0]);
-
-    /* Mark the endpoints as not yet open */
-    for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
-        dev->ep_fd[i] = -1;
-    }
-
-    ioctl(dfd, USB_SETDEBUG, &ugendebug);
-
-    ret = (USBDevice *)dev;
-
-fail_dfd:
-    close(dfd);
-fail_bfd:
-    close(bfd);
-fail:
-    return ret;
-}
-
-static void usb_host_class_initfn(ObjectClass *klass, void *data)
-{
-    USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
-
-    uc->product_desc   = "USB Host Device";
-    uc->init           = usb_host_initfn;
-    uc->handle_reset   = usb_host_handle_reset;
-    uc->handle_control = usb_host_handle_control;
-    uc->handle_data    = usb_host_handle_data;
-    uc->handle_destroy = usb_host_handle_destroy;
-}
-
-static const TypeInfo usb_host_dev_info = {
-    .name          = "usb-host",
-    .parent        = TYPE_USB_DEVICE,
-    .instance_size = sizeof(USBHostDevice),
-    .class_init    = usb_host_class_initfn,
-};
-
-static void usb_host_register_types(void)
-{
-    type_register_static(&usb_host_dev_info);
-}
-
-type_init(usb_host_register_types)
-
-static int usb_host_scan(void *opaque, USBScanFunc *func)
-{
-    struct usb_device_info bus_info;
-    struct usb_device_info dev_info;
-    uint16_t vendor_id, product_id, class_id, speed;
-    int bfd, dfd, bus, address;
-    char busbuf[20], devbuf[20], product_name[256];
-    int ret = 0;
-
-    for (bus = 0; bus < 10; bus++) {
-
-        snprintf(busbuf, sizeof(busbuf) - 1, "/dev/usb%d", bus);
-        bfd = open(busbuf, O_RDWR);
-        if (bfd < 0)
-           continue;
-
-        for (address = 1; address < 127; address++) {
-
-            bus_info.udi_addr = address;
-            if (ioctl(bfd, USB_DEVICEINFO, &bus_info) < 0)
-                continue;
-
-            /* only list devices that can be used by generic layer */
-            if (strncmp(bus_info.udi_devnames[0], "ugen", 4) != 0)
-                continue;
-
-#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
-            snprintf(devbuf, sizeof(devbuf) - 1, "/dev/%s", bus_info.udi_devnames[0]);
-#else
-            snprintf(devbuf, sizeof(devbuf) - 1, "/dev/%s.00", bus_info.udi_devnames[0]);
-#endif
-
-            dfd = open(devbuf, O_RDONLY);
-            if (dfd < 0) {
-#ifdef DEBUG
-                printf("usb_host_scan: couldn't open device %s - %s\n", devbuf,
-                       strerror(errno));
-#endif
-                continue;
-            }
-
-            if (ioctl(dfd, USB_GET_DEVICEINFO, &dev_info) < 0)
-                printf("usb_host_scan: couldn't get device information for %s - %s\n",
-                       devbuf, strerror(errno));
-
-            /* XXX: might need to fixup endianness of word values before copying over */
-
-            vendor_id = dev_info.udi_vendorNo;
-            product_id = dev_info.udi_productNo;
-            class_id = dev_info.udi_class;
-            speed = dev_info.udi_speed;
-
-            if (strncmp(dev_info.udi_product, "product", 7) != 0)
-                pstrcpy(product_name, sizeof(product_name),
-                        dev_info.udi_product);
-            else
-                product_name[0] = '\0';
-
-            ret = func(opaque, bus, address, class_id, vendor_id,
-                       product_id, product_name, speed);
-
-            close(dfd);
-
-            if (ret)
-                goto the_end;
-        }
-
-        close(bfd);
-    }
-
-the_end:
-    return ret;
-}
-
-typedef struct FindDeviceState {
-    int vendor_id;
-    int product_id;
-    int bus_num;
-    int addr;
-} FindDeviceState;
-
-static int usb_host_find_device_scan(void *opaque, int bus_num, int addr,
-                                     int class_id,
-                                     int vendor_id, int product_id,
-                                     const char *product_name, int speed)
-{
-    FindDeviceState *s = opaque;
-    if (vendor_id == s->vendor_id &&
-        product_id == s->product_id) {
-        s->bus_num = bus_num;
-        s->addr = addr;
-        return 1;
-     } else {
-        return 0;
-     }
-}
-
-
-/* the syntax is :
-   'bus.addr' (decimal numbers) or
-   'vendor_id:product_id' (hexa numbers) */
-static int usb_host_find_device(int *pbus_num, int *paddr,
-                                const char *devname)
-{
-    const char *p;
-    int ret;
-    FindDeviceState fs;
-
-    p = strchr(devname, '.');
-    if (p) {
-        *pbus_num = strtoul(devname, NULL, 0);
-        *paddr = strtoul(p + 1, NULL, 0);
-        return 0;
-    }
-    p = strchr(devname, ':');
-    if (p) {
-        fs.vendor_id = strtoul(devname, NULL, 16);
-        fs.product_id = strtoul(p + 1, NULL, 16);
-        ret = usb_host_scan(&fs, usb_host_find_device_scan);
-        if (ret) {
-            *pbus_num = fs.bus_num;
-            *paddr = fs.addr;
-            return 0;
-        }
-     }
-     return -1;
-}
-
-/**********************/
-/* USB host device info */
-
-struct usb_class_info {
-    int class;
-    const char *class_name;
-};
-
-static const struct usb_class_info usb_class_info[] = {
-    { USB_CLASS_AUDIO, "Audio"},
-    { USB_CLASS_COMM, "Communication"},
-    { USB_CLASS_HID, "HID"},
-    { USB_CLASS_HUB, "Hub" },
-    { USB_CLASS_PHYSICAL, "Physical" },
-    { USB_CLASS_PRINTER, "Printer" },
-    { USB_CLASS_MASS_STORAGE, "Storage" },
-    { USB_CLASS_CDC_DATA, "Data" },
-    { USB_CLASS_APP_SPEC, "Application Specific" },
-    { USB_CLASS_VENDOR_SPEC, "Vendor Specific" },
-    { USB_CLASS_STILL_IMAGE, "Still Image" },
-    { USB_CLASS_CSCID, "Smart Card" },
-    { USB_CLASS_CONTENT_SEC, "Content Security" },
-    { -1, NULL }
-};
-
-static const char *usb_class_str(uint8_t class)
-{
-    const struct usb_class_info *p;
-    for (p = usb_class_info; p->class != -1; p++) {
-        if (p->class == class)
-            break;
-    }
-    return p->class_name;
-}
-
-static void usb_info_device(Monitor *mon, int bus_num, int addr, int class_id,
-                            int vendor_id, int product_id,
-                            const char *product_name,
-                            int speed)
-{
-    const char *class_str, *speed_str;
-
-    switch(speed) {
-    case USB_SPEED_LOW:
-        speed_str = "1.5";
-        break;
-    case USB_SPEED_FULL:
-        speed_str = "12";
-        break;
-    case USB_SPEED_HIGH:
-        speed_str = "480";
-        break;
-    default:
-        speed_str = "?";
-        break;
-    }
-
-    monitor_printf(mon, "  Device %d.%d, speed %s Mb/s\n",
-                   bus_num, addr, speed_str);
-    class_str = usb_class_str(class_id);
-    if (class_str)
-        monitor_printf(mon, "    %s:", class_str);
-    else
-        monitor_printf(mon, "    Class %02x:", class_id);
-    monitor_printf(mon, " USB device %04x:%04x", vendor_id, product_id);
-    if (product_name[0] != '\0')
-        monitor_printf(mon, ", %s", product_name);
-    monitor_printf(mon, "\n");
-}
-
-static int usb_host_info_device(void *opaque,
-                                int bus_num, int addr,
-                                int class_id,
-                                int vendor_id, int product_id,
-                                const char *product_name,
-                                int speed)
-{
-    Monitor *mon = opaque;
-
-    usb_info_device(mon, bus_num, addr, class_id, vendor_id, product_id,
-                    product_name, speed);
-    return 0;
-}
-
-void usb_host_info(Monitor *mon, const QDict *qdict)
-{
-    usb_host_scan(mon, usb_host_info_device);
-}
diff --git a/hw/usb/host-linux.c b/hw/usb/host-linux.c
deleted file mode 100644 (file)
index 65cd3b4..0000000
+++ /dev/null
@@ -1,1911 +0,0 @@
-/*
- * Linux host USB redirector
- *
- * Copyright (c) 2005 Fabrice Bellard
- *
- * Copyright (c) 2008 Max Krasnyansky
- *      Support for host device auto connect & disconnect
- *      Major rewrite to support fully async operation
- *
- * Copyright 2008 TJ <linux@tjworld.net>
- *      Added flexible support for /dev/bus/usb /sys/bus/usb/devices in addition
- *      to the legacy /proc/bus/usb USB device discovery and handling
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "qemu-common.h"
-#include "qemu/timer.h"
-#include "monitor/monitor.h"
-#include "sysemu/sysemu.h"
-#include "trace.h"
-
-#include <dirent.h>
-#include <sys/ioctl.h>
-
-#include <linux/usbdevice_fs.h>
-#include <linux/version.h>
-#include "hw/usb.h"
-#include "hw/usb/desc.h"
-#include "hw/usb/host.h"
-
-#ifdef CONFIG_USB_LIBUSB
-# define DEVNAME "usb-host-linux"
-#else
-# define DEVNAME "usb-host"
-#endif
-
-/* We redefine it to avoid version problems */
-struct usb_ctrltransfer {
-    uint8_t  bRequestType;
-    uint8_t  bRequest;
-    uint16_t wValue;
-    uint16_t wIndex;
-    uint16_t wLength;
-    uint32_t timeout;
-    void *data;
-};
-
-typedef int USBScanFunc(void *opaque, int bus_num, int addr, const char *port,
-                        int class_id, int vendor_id, int product_id,
-                        const char *product_name, int speed);
-
-//#define DEBUG
-
-#ifdef DEBUG
-#define DPRINTF printf
-#else
-#define DPRINTF(...)
-#endif
-
-#define PRODUCT_NAME_SZ 32
-#define MAX_PORTLEN 16
-
-/* endpoint association data */
-#define ISO_FRAME_DESC_PER_URB 32
-
-/* devio.c limits single requests to 16k */
-#define MAX_USBFS_BUFFER_SIZE 16384
-
-typedef struct AsyncURB AsyncURB;
-
-struct endp_data {
-    uint8_t halted;
-    uint8_t iso_started;
-    AsyncURB *iso_urb;
-    int iso_urb_idx;
-    int iso_buffer_used;
-    int inflight;
-};
-
-enum USBHostDeviceOptions {
-    USB_HOST_OPT_PIPELINE,
-};
-
-typedef struct USBHostDevice {
-    USBDevice dev;
-    int       fd;
-    int       hub_fd;
-    int       hub_port;
-
-    uint8_t   descr[8192];
-    int       descr_len;
-    int       closing;
-    uint32_t  iso_urb_count;
-    uint32_t  options;
-    Notifier  exit;
-    QEMUBH    *bh;
-
-    struct endp_data ep_in[USB_MAX_ENDPOINTS];
-    struct endp_data ep_out[USB_MAX_ENDPOINTS];
-    QLIST_HEAD(, AsyncURB) aurbs;
-
-    /* Host side address */
-    int bus_num;
-    int addr;
-    char port[MAX_PORTLEN];
-    struct USBAutoFilter match;
-    int32_t bootindex;
-    int seen, errcount;
-
-    QTAILQ_ENTRY(USBHostDevice) next;
-} USBHostDevice;
-
-static QTAILQ_HEAD(, USBHostDevice) hostdevs = QTAILQ_HEAD_INITIALIZER(hostdevs);
-
-static int usb_host_close(USBHostDevice *dev);
-static void usb_host_auto_check(void *unused);
-static int usb_host_read_file(char *line, size_t line_size,
-                            const char *device_file, const char *device_name);
-static void usb_linux_update_endp_table(USBHostDevice *s);
-
-static int usb_host_usbfs_type(USBHostDevice *s, USBPacket *p)
-{
-    static const int usbfs[] = {
-        [USB_ENDPOINT_XFER_CONTROL] = USBDEVFS_URB_TYPE_CONTROL,
-        [USB_ENDPOINT_XFER_ISOC]    = USBDEVFS_URB_TYPE_ISO,
-        [USB_ENDPOINT_XFER_BULK]    = USBDEVFS_URB_TYPE_BULK,
-        [USB_ENDPOINT_XFER_INT]     = USBDEVFS_URB_TYPE_INTERRUPT,
-    };
-    uint8_t type = p->ep->type;
-    assert(type < ARRAY_SIZE(usbfs));
-    return usbfs[type];
-}
-
-static int usb_host_do_reset(USBHostDevice *dev)
-{
-    struct timeval s, e;
-    uint32_t usecs;
-    int ret;
-
-    gettimeofday(&s, NULL);
-    ret = ioctl(dev->fd, USBDEVFS_RESET);
-    gettimeofday(&e, NULL);
-    usecs = (e.tv_sec  - s.tv_sec) * 1000000;
-    usecs += e.tv_usec - s.tv_usec;
-    if (usecs > 1000000) {
-        /* more than a second, something is fishy, broken usb device? */
-        fprintf(stderr, "husb: device %d:%d reset took %d.%06d seconds\n",
-                dev->bus_num, dev->addr, usecs / 1000000, usecs % 1000000);
-    }
-    return ret;
-}
-
-static struct endp_data *get_endp(USBHostDevice *s, int pid, int ep)
-{
-    struct endp_data *eps = pid == USB_TOKEN_IN ? s->ep_in : s->ep_out;
-    assert(pid == USB_TOKEN_IN || pid == USB_TOKEN_OUT);
-    assert(ep > 0 && ep <= USB_MAX_ENDPOINTS);
-    return eps + ep - 1;
-}
-
-static int is_isoc(USBHostDevice *s, int pid, int ep)
-{
-    return usb_ep_get_type(&s->dev, pid, ep) == USB_ENDPOINT_XFER_ISOC;
-}
-
-static int is_valid(USBHostDevice *s, int pid, int ep)
-{
-    return usb_ep_get_type(&s->dev, pid, ep) != USB_ENDPOINT_XFER_INVALID;
-}
-
-static int is_halted(USBHostDevice *s, int pid, int ep)
-{
-    return get_endp(s, pid, ep)->halted;
-}
-
-static void clear_halt(USBHostDevice *s, int pid, int ep)
-{
-    trace_usb_host_ep_clear_halt(s->bus_num, s->addr, ep);
-    get_endp(s, pid, ep)->halted = 0;
-}
-
-static void set_halt(USBHostDevice *s, int pid, int ep)
-{
-    if (ep != 0) {
-        trace_usb_host_ep_set_halt(s->bus_num, s->addr, ep);
-        get_endp(s, pid, ep)->halted = 1;
-    }
-}
-
-static int is_iso_started(USBHostDevice *s, int pid, int ep)
-{
-    return get_endp(s, pid, ep)->iso_started;
-}
-
-static void clear_iso_started(USBHostDevice *s, int pid, int ep)
-{
-    trace_usb_host_iso_stop(s->bus_num, s->addr, ep);
-    get_endp(s, pid, ep)->iso_started = 0;
-}
-
-static void set_iso_started(USBHostDevice *s, int pid, int ep)
-{
-    struct endp_data *e = get_endp(s, pid, ep);
-
-    trace_usb_host_iso_start(s->bus_num, s->addr, ep);
-    if (!e->iso_started) {
-        e->iso_started = 1;
-        e->inflight = 0;
-    }
-}
-
-static int change_iso_inflight(USBHostDevice *s, int pid, int ep, int value)
-{
-    struct endp_data *e = get_endp(s, pid, ep);
-
-    e->inflight += value;
-    return e->inflight;
-}
-
-static void set_iso_urb(USBHostDevice *s, int pid, int ep, AsyncURB *iso_urb)
-{
-    get_endp(s, pid, ep)->iso_urb = iso_urb;
-}
-
-static AsyncURB *get_iso_urb(USBHostDevice *s, int pid, int ep)
-{
-    return get_endp(s, pid, ep)->iso_urb;
-}
-
-static void set_iso_urb_idx(USBHostDevice *s, int pid, int ep, int i)
-{
-    get_endp(s, pid, ep)->iso_urb_idx = i;
-}
-
-static int get_iso_urb_idx(USBHostDevice *s, int pid, int ep)
-{
-    return get_endp(s, pid, ep)->iso_urb_idx;
-}
-
-static void set_iso_buffer_used(USBHostDevice *s, int pid, int ep, int i)
-{
-    get_endp(s, pid, ep)->iso_buffer_used = i;
-}
-
-static int get_iso_buffer_used(USBHostDevice *s, int pid, int ep)
-{
-    return get_endp(s, pid, ep)->iso_buffer_used;
-}
-
-/*
- * Async URB state.
- * We always allocate iso packet descriptors even for bulk transfers
- * to simplify allocation and casts.
- */
-struct AsyncURB
-{
-    struct usbdevfs_urb urb;
-    struct usbdevfs_iso_packet_desc isocpd[ISO_FRAME_DESC_PER_URB];
-    USBHostDevice *hdev;
-    QLIST_ENTRY(AsyncURB) next;
-
-    /* For regular async urbs */
-    USBPacket     *packet;
-    int more; /* large transfer, more urbs follow */
-
-    /* For buffered iso handling */
-    int iso_frame_idx; /* -1 means in flight */
-};
-
-static AsyncURB *async_alloc(USBHostDevice *s)
-{
-    AsyncURB *aurb = g_malloc0(sizeof(AsyncURB));
-    aurb->hdev = s;
-    QLIST_INSERT_HEAD(&s->aurbs, aurb, next);
-    return aurb;
-}
-
-static void async_free(AsyncURB *aurb)
-{
-    QLIST_REMOVE(aurb, next);
-    g_free(aurb);
-}
-
-static void do_disconnect(USBHostDevice *s)
-{
-    usb_host_close(s);
-    usb_host_auto_check(NULL);
-}
-
-static void async_complete(void *opaque)
-{
-    USBHostDevice *s = opaque;
-    AsyncURB *aurb;
-    int urbs = 0;
-
-    while (1) {
-        USBPacket *p;
-
-        int r = ioctl(s->fd, USBDEVFS_REAPURBNDELAY, &aurb);
-        if (r < 0) {
-            if (errno == EAGAIN) {
-                if (urbs > 2) {
-                    /* indicates possible latency issues */
-                    trace_usb_host_iso_many_urbs(s->bus_num, s->addr, urbs);
-                }
-                return;
-            }
-            if (errno == ENODEV) {
-                if (!s->closing) {
-                    trace_usb_host_disconnect(s->bus_num, s->addr);
-                    do_disconnect(s);
-                }
-                return;
-            }
-
-            perror("USBDEVFS_REAPURBNDELAY");
-            return;
-        }
-
-        DPRINTF("husb: async completed. aurb %p status %d alen %d\n",
-                aurb, aurb->urb.status, aurb->urb.actual_length);
-
-        /* If this is a buffered iso urb mark it as complete and don't do
-           anything else (it is handled further in usb_host_handle_iso_data) */
-        if (aurb->iso_frame_idx == -1) {
-            int inflight;
-            int pid = (aurb->urb.endpoint & USB_DIR_IN) ?
-                USB_TOKEN_IN : USB_TOKEN_OUT;
-            int ep = aurb->urb.endpoint & 0xf;
-            if (aurb->urb.status == -EPIPE) {
-                set_halt(s, pid, ep);
-            }
-            aurb->iso_frame_idx = 0;
-            urbs++;
-            inflight = change_iso_inflight(s, pid, ep, -1);
-            if (inflight == 0 && is_iso_started(s, pid, ep)) {
-                /* can be latency issues, or simply end of stream */
-                trace_usb_host_iso_out_of_bufs(s->bus_num, s->addr, ep);
-            }
-            continue;
-        }
-
-        p = aurb->packet;
-        trace_usb_host_urb_complete(s->bus_num, s->addr, aurb, aurb->urb.status,
-                                    aurb->urb.actual_length, aurb->more);
-
-        if (p) {
-            switch (aurb->urb.status) {
-            case 0:
-                p->actual_length += aurb->urb.actual_length;
-                if (!aurb->more) {
-                    /* Clear previous ASYNC status */
-                    p->status = USB_RET_SUCCESS;
-                }
-                break;
-
-            case -EPIPE:
-                set_halt(s, p->pid, p->ep->nr);
-                p->status = USB_RET_STALL;
-                break;
-
-            case -EOVERFLOW:
-                p->status = USB_RET_BABBLE;
-                break;
-
-            default:
-                p->status = USB_RET_IOERROR;
-                break;
-            }
-
-            if (aurb->urb.type == USBDEVFS_URB_TYPE_CONTROL) {
-                trace_usb_host_req_complete(s->bus_num, s->addr, p,
-                                            p->status, aurb->urb.actual_length);
-                usb_generic_async_ctrl_complete(&s->dev, p);
-            } else if (!aurb->more) {
-                trace_usb_host_req_complete(s->bus_num, s->addr, p,
-                                            p->status, aurb->urb.actual_length);
-                usb_packet_complete(&s->dev, p);
-            }
-        }
-
-        async_free(aurb);
-    }
-}
-
-static void usb_host_async_cancel(USBDevice *dev, USBPacket *p)
-{
-    USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
-    AsyncURB *aurb;
-
-    trace_usb_host_req_canceled(s->bus_num, s->addr, p);
-
-    QLIST_FOREACH(aurb, &s->aurbs, next) {
-        if (p != aurb->packet) {
-            continue;
-        }
-
-        trace_usb_host_urb_canceled(s->bus_num, s->addr, aurb);
-
-        /* Mark it as dead (see async_complete above) */
-        aurb->packet = NULL;
-
-        int r = ioctl(s->fd, USBDEVFS_DISCARDURB, aurb);
-        if (r < 0) {
-            DPRINTF("husb: async. discard urb failed errno %d\n", errno);
-        }
-    }
-}
-
-static int usb_host_open_device(int bus, int addr)
-{
-    const char *usbfs = NULL;
-    char filename[32];
-    struct stat st;
-    int fd, rc;
-
-    rc = stat("/dev/bus/usb", &st);
-    if (rc == 0 && S_ISDIR(st.st_mode)) {
-        /* udev-created device nodes available */
-        usbfs = "/dev/bus/usb";
-    } else {
-        /* fallback: usbfs mounted below /proc */
-        usbfs = "/proc/bus/usb";
-    }
-
-    snprintf(filename, sizeof(filename), "%s/%03d/%03d",
-             usbfs, bus, addr);
-    fd = open(filename, O_RDWR | O_NONBLOCK);
-    if (fd < 0) {
-        fprintf(stderr, "husb: open %s: %s\n", filename, strerror(errno));
-    }
-    return fd;
-}
-
-static int usb_host_claim_port(USBHostDevice *s)
-{
-#ifdef USBDEVFS_CLAIM_PORT
-    char *h, hub_name[64], line[1024];
-    int hub_addr, ret;
-
-    snprintf(hub_name, sizeof(hub_name), "%d-%s",
-             s->match.bus_num, s->match.port);
-
-    /* try strip off last ".$portnr" to get hub */
-    h = strrchr(hub_name, '.');
-    if (h != NULL) {
-        s->hub_port = atoi(h+1);
-        *h = '\0';
-    } else {
-        /* no dot in there -> it is the root hub */
-        snprintf(hub_name, sizeof(hub_name), "usb%d",
-                 s->match.bus_num);
-        s->hub_port = atoi(s->match.port);
-    }
-
-    if (!usb_host_read_file(line, sizeof(line), "devnum",
-                            hub_name)) {
-        return -1;
-    }
-    if (sscanf(line, "%d", &hub_addr) != 1) {
-        return -1;
-    }
-
-    s->hub_fd = usb_host_open_device(s->match.bus_num, hub_addr);
-    if (s->hub_fd < 0) {
-        return -1;
-    }
-
-    ret = ioctl(s->hub_fd, USBDEVFS_CLAIM_PORT, &s->hub_port);
-    if (ret < 0) {
-        close(s->hub_fd);
-        s->hub_fd = -1;
-        return -1;
-    }
-
-    trace_usb_host_claim_port(s->match.bus_num, hub_addr, s->hub_port);
-    return 0;
-#else
-    return -1;
-#endif
-}
-
-static void usb_host_release_port(USBHostDevice *s)
-{
-    if (s->hub_fd == -1) {
-        return;
-    }
-#ifdef USBDEVFS_RELEASE_PORT
-    ioctl(s->hub_fd, USBDEVFS_RELEASE_PORT, &s->hub_port);
-#endif
-    close(s->hub_fd);
-    s->hub_fd = -1;
-}
-
-static int usb_host_disconnect_ifaces(USBHostDevice *dev, int nb_interfaces)
-{
-    /* earlier Linux 2.4 do not support that */
-#ifdef USBDEVFS_DISCONNECT
-    struct usbdevfs_ioctl ctrl;
-    int ret, interface;
-
-    for (interface = 0; interface < nb_interfaces; interface++) {
-        ctrl.ioctl_code = USBDEVFS_DISCONNECT;
-        ctrl.ifno = interface;
-        ctrl.data = 0;
-        ret = ioctl(dev->fd, USBDEVFS_IOCTL, &ctrl);
-        if (ret < 0 && errno != ENODATA) {
-            perror("USBDEVFS_DISCONNECT");
-            return -1;
-        }
-    }
-#endif
-    return 0;
-}
-
-static int usb_linux_get_num_interfaces(USBHostDevice *s)
-{
-    char device_name[64], line[1024];
-    int num_interfaces = 0;
-
-    sprintf(device_name, "%d-%s", s->bus_num, s->port);
-    if (!usb_host_read_file(line, sizeof(line), "bNumInterfaces",
-                            device_name)) {
-        return -1;
-    }
-    if (sscanf(line, "%d", &num_interfaces) != 1) {
-        return -1;
-    }
-    return num_interfaces;
-}
-
-static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration)
-{
-    const char *op = NULL;
-    int dev_descr_len, config_descr_len;
-    int interface, nb_interfaces;
-    int ret, i;
-
-    for (i = 0; i < USB_MAX_INTERFACES; i++) {
-        dev->dev.altsetting[i] = 0;
-    }
-
-    if (configuration == 0) { /* address state - ignore */
-        dev->dev.ninterfaces   = 0;
-        dev->dev.configuration = 0;
-        return 1;
-    }
-
-    DPRINTF("husb: claiming interfaces. config %d\n", configuration);
-
-    i = 0;
-    dev_descr_len = dev->descr[0];
-    if (dev_descr_len > dev->descr_len) {
-        fprintf(stderr, "husb: update iface failed. descr too short\n");
-        return 0;
-    }
-
-    i += dev_descr_len;
-    while (i < dev->descr_len) {
-        DPRINTF("husb: i is %d, descr_len is %d, dl %d, dt %d\n",
-                i, dev->descr_len,
-               dev->descr[i], dev->descr[i+1]);
-
-        if (dev->descr[i+1] != USB_DT_CONFIG) {
-            i += dev->descr[i];
-            continue;
-        }
-        config_descr_len = dev->descr[i];
-
-        DPRINTF("husb: config #%d need %d\n", dev->descr[i + 5], configuration);
-
-        if (configuration == dev->descr[i + 5]) {
-            configuration = dev->descr[i + 5];
-            break;
-        }
-
-        i += config_descr_len;
-    }
-
-    if (i >= dev->descr_len) {
-        fprintf(stderr,
-                "husb: update iface failed. no matching configuration\n");
-        return 0;
-    }
-    nb_interfaces = dev->descr[i + 4];
-
-    if (usb_host_disconnect_ifaces(dev, nb_interfaces) < 0) {
-        goto fail;
-    }
-
-    /* XXX: only grab if all interfaces are free */
-    for (interface = 0; interface < nb_interfaces; interface++) {
-        op = "USBDEVFS_CLAIMINTERFACE";
-        ret = ioctl(dev->fd, USBDEVFS_CLAIMINTERFACE, &interface);
-        if (ret < 0) {
-            goto fail;
-        }
-    }
-
-    trace_usb_host_claim_interfaces(dev->bus_num, dev->addr,
-                                    nb_interfaces, configuration);
-
-    dev->dev.ninterfaces   = nb_interfaces;
-    dev->dev.configuration = configuration;
-    return 1;
-
-fail:
-    if (errno == ENODEV) {
-        do_disconnect(dev);
-    }
-    perror(op);
-    return 0;
-}
-
-static int usb_host_release_interfaces(USBHostDevice *s)
-{
-    int ret, i;
-
-    trace_usb_host_release_interfaces(s->bus_num, s->addr);
-
-    for (i = 0; i < s->dev.ninterfaces; i++) {
-        ret = ioctl(s->fd, USBDEVFS_RELEASEINTERFACE, &i);
-        if (ret < 0) {
-            perror("USBDEVFS_RELEASEINTERFACE");
-            return 0;
-        }
-    }
-    return 1;
-}
-
-static void usb_host_handle_reset(USBDevice *dev)
-{
-    USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
-
-    trace_usb_host_reset(s->bus_num, s->addr);
-
-    usb_host_do_reset(s);
-
-    usb_host_claim_interfaces(s, 0);
-    usb_linux_update_endp_table(s);
-}
-
-static void usb_host_handle_destroy(USBDevice *dev)
-{
-    USBHostDevice *s = (USBHostDevice *)dev;
-
-    usb_host_release_port(s);
-    usb_host_close(s);
-    QTAILQ_REMOVE(&hostdevs, s, next);
-    qemu_remove_exit_notifier(&s->exit);
-}
-
-/* iso data is special, we need to keep enough urbs in flight to make sure
-   that the controller never runs out of them, otherwise the device will
-   likely suffer a buffer underrun / overrun. */
-static AsyncURB *usb_host_alloc_iso(USBHostDevice *s, int pid, uint8_t ep)
-{
-    AsyncURB *aurb;
-    int i, j, len = usb_ep_get_max_packet_size(&s->dev, pid, ep);
-
-    aurb = g_malloc0(s->iso_urb_count * sizeof(*aurb));
-    for (i = 0; i < s->iso_urb_count; i++) {
-        aurb[i].urb.endpoint      = ep;
-        aurb[i].urb.buffer_length = ISO_FRAME_DESC_PER_URB * len;
-        aurb[i].urb.buffer        = g_malloc(aurb[i].urb.buffer_length);
-        aurb[i].urb.type          = USBDEVFS_URB_TYPE_ISO;
-        aurb[i].urb.flags         = USBDEVFS_URB_ISO_ASAP;
-        aurb[i].urb.number_of_packets = ISO_FRAME_DESC_PER_URB;
-        for (j = 0 ; j < ISO_FRAME_DESC_PER_URB; j++)
-            aurb[i].urb.iso_frame_desc[j].length = len;
-        if (pid == USB_TOKEN_IN) {
-            aurb[i].urb.endpoint |= 0x80;
-            /* Mark as fully consumed (idle) */
-            aurb[i].iso_frame_idx = ISO_FRAME_DESC_PER_URB;
-        }
-    }
-    set_iso_urb(s, pid, ep, aurb);
-
-    return aurb;
-}
-
-static void usb_host_stop_n_free_iso(USBHostDevice *s, int pid, uint8_t ep)
-{
-    AsyncURB *aurb;
-    int i, ret, killed = 0, free = 1;
-
-    aurb = get_iso_urb(s, pid, ep);
-    if (!aurb) {
-        return;
-    }
-
-    for (i = 0; i < s->iso_urb_count; i++) {
-        /* in flight? */
-        if (aurb[i].iso_frame_idx == -1) {
-            ret = ioctl(s->fd, USBDEVFS_DISCARDURB, &aurb[i]);
-            if (ret < 0) {
-                perror("USBDEVFS_DISCARDURB");
-                free = 0;
-                continue;
-            }
-            killed++;
-        }
-    }
-
-    /* Make sure any urbs we've killed are reaped before we free them */
-    if (killed) {
-        async_complete(s);
-    }
-
-    for (i = 0; i < s->iso_urb_count; i++) {
-        g_free(aurb[i].urb.buffer);
-    }
-
-    if (free)
-        g_free(aurb);
-    else
-        printf("husb: leaking iso urbs because of discard failure\n");
-    set_iso_urb(s, pid, ep, NULL);
-    set_iso_urb_idx(s, pid, ep, 0);
-    clear_iso_started(s, pid, ep);
-}
-
-static void urb_status_to_usb_ret(int status, USBPacket *p)
-{
-    switch (status) {
-    case -EPIPE:
-        p->status = USB_RET_STALL;
-        break;
-    case -EOVERFLOW:
-        p->status = USB_RET_BABBLE;
-        break;
-    default:
-        p->status = USB_RET_IOERROR;
-    }
-}
-
-static void usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in)
-{
-    AsyncURB *aurb;
-    int i, j, max_packet_size, offset, len;
-    uint8_t *buf;
-
-    max_packet_size = p->ep->max_packet_size;
-    if (max_packet_size == 0) {
-        p->status = USB_RET_NAK;
-        return;
-    }
-
-    aurb = get_iso_urb(s, p->pid, p->ep->nr);
-    if (!aurb) {
-        aurb = usb_host_alloc_iso(s, p->pid, p->ep->nr);
-    }
-
-    i = get_iso_urb_idx(s, p->pid, p->ep->nr);
-    j = aurb[i].iso_frame_idx;
-    if (j >= 0 && j < ISO_FRAME_DESC_PER_URB) {
-        if (in) {
-            /* Check urb status  */
-            if (aurb[i].urb.status) {
-                urb_status_to_usb_ret(aurb[i].urb.status, p);
-                /* Move to the next urb */
-                aurb[i].iso_frame_idx = ISO_FRAME_DESC_PER_URB - 1;
-            /* Check frame status */
-            } else if (aurb[i].urb.iso_frame_desc[j].status) {
-                urb_status_to_usb_ret(aurb[i].urb.iso_frame_desc[j].status, p);
-            /* Check the frame fits */
-            } else if (aurb[i].urb.iso_frame_desc[j].actual_length
-                       > p->iov.size) {
-                printf("husb: received iso data is larger then packet\n");
-                p->status = USB_RET_BABBLE;
-            /* All good copy data over */
-            } else {
-                len = aurb[i].urb.iso_frame_desc[j].actual_length;
-                buf  = aurb[i].urb.buffer +
-                    j * aurb[i].urb.iso_frame_desc[0].length;
-                usb_packet_copy(p, buf, len);
-            }
-        } else {
-            len = p->iov.size;
-            offset = (j == 0) ? 0 : get_iso_buffer_used(s, p->pid, p->ep->nr);
-
-            /* Check the frame fits */
-            if (len > max_packet_size) {
-                printf("husb: send iso data is larger then max packet size\n");
-                p->status = USB_RET_NAK;
-                return;
-            }
-
-            /* All good copy data over */
-            usb_packet_copy(p, aurb[i].urb.buffer + offset, len);
-            aurb[i].urb.iso_frame_desc[j].length = len;
-            offset += len;
-            set_iso_buffer_used(s, p->pid, p->ep->nr, offset);
-
-            /* Start the stream once we have buffered enough data */
-            if (!is_iso_started(s, p->pid, p->ep->nr) && i == 1 && j == 8) {
-                set_iso_started(s, p->pid, p->ep->nr);
-            }
-        }
-        aurb[i].iso_frame_idx++;
-        if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) {
-            i = (i + 1) % s->iso_urb_count;
-            set_iso_urb_idx(s, p->pid, p->ep->nr, i);
-        }
-    } else {
-        if (in) {
-            set_iso_started(s, p->pid, p->ep->nr);
-        } else {
-            DPRINTF("hubs: iso out error no free buffer, dropping packet\n");
-        }
-    }
-
-    if (is_iso_started(s, p->pid, p->ep->nr)) {
-        /* (Re)-submit all fully consumed / filled urbs */
-        for (i = 0; i < s->iso_urb_count; i++) {
-            if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) {
-                if (ioctl(s->fd, USBDEVFS_SUBMITURB, &aurb[i]) < 0) {
-                    perror("USBDEVFS_SUBMITURB");
-                    if (!in || p->status == USB_RET_SUCCESS) {
-                        switch(errno) {
-                        case ETIMEDOUT:
-                            p->status = USB_RET_NAK;
-                            break;
-                        case EPIPE:
-                        default:
-                            p->status = USB_RET_STALL;
-                        }
-                    }
-                    break;
-                }
-                aurb[i].iso_frame_idx = -1;
-                change_iso_inflight(s, p->pid, p->ep->nr, 1);
-            }
-        }
-    }
-}
-
-static void usb_host_handle_data(USBDevice *dev, USBPacket *p)
-{
-    USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
-    struct usbdevfs_urb *urb;
-    AsyncURB *aurb;
-    int ret, rem, prem, v;
-    uint8_t *pbuf;
-    uint8_t ep;
-
-    trace_usb_host_req_data(s->bus_num, s->addr, p,
-                            p->pid == USB_TOKEN_IN,
-                            p->ep->nr, p->iov.size);
-
-    if (!is_valid(s, p->pid, p->ep->nr)) {
-        p->status = USB_RET_NAK;
-        trace_usb_host_req_complete(s->bus_num, s->addr, p,
-                                    p->status, p->actual_length);
-        return;
-    }
-
-    if (p->pid == USB_TOKEN_IN) {
-        ep = p->ep->nr | 0x80;
-    } else {
-        ep = p->ep->nr;
-    }
-
-    if (is_halted(s, p->pid, p->ep->nr)) {
-        unsigned int arg = ep;
-        ret = ioctl(s->fd, USBDEVFS_CLEAR_HALT, &arg);
-        if (ret < 0) {
-            perror("USBDEVFS_CLEAR_HALT");
-            p->status = USB_RET_NAK;
-            trace_usb_host_req_complete(s->bus_num, s->addr, p,
-                                        p->status, p->actual_length);
-            return;
-        }
-        clear_halt(s, p->pid, p->ep->nr);
-    }
-
-    if (is_isoc(s, p->pid, p->ep->nr)) {
-        usb_host_handle_iso_data(s, p, p->pid == USB_TOKEN_IN);
-        return;
-    }
-
-    v = 0;
-    prem = 0;
-    pbuf = NULL;
-    rem = p->iov.size;
-    do {
-        if (prem == 0 && rem > 0) {
-            assert(v < p->iov.niov);
-            prem = p->iov.iov[v].iov_len;
-            pbuf = p->iov.iov[v].iov_base;
-            assert(prem <= rem);
-            v++;
-        }
-        aurb = async_alloc(s);
-        aurb->packet = p;
-
-        urb = &aurb->urb;
-        urb->endpoint      = ep;
-        urb->type          = usb_host_usbfs_type(s, p);
-        urb->usercontext   = s;
-        urb->buffer        = pbuf;
-        urb->buffer_length = prem;
-
-        if (urb->buffer_length > MAX_USBFS_BUFFER_SIZE) {
-            urb->buffer_length = MAX_USBFS_BUFFER_SIZE;
-        }
-        pbuf += urb->buffer_length;
-        prem -= urb->buffer_length;
-        rem  -= urb->buffer_length;
-        if (rem) {
-            aurb->more         = 1;
-        }
-
-        trace_usb_host_urb_submit(s->bus_num, s->addr, aurb,
-                                  urb->buffer_length, aurb->more);
-        ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb);
-
-        DPRINTF("husb: data submit: ep 0x%x, len %u, more %d, packet %p, aurb %p\n",
-                urb->endpoint, urb->buffer_length, aurb->more, p, aurb);
-
-        if (ret < 0) {
-            perror("USBDEVFS_SUBMITURB");
-            async_free(aurb);
-
-            switch(errno) {
-            case ETIMEDOUT:
-                p->status = USB_RET_NAK;
-                trace_usb_host_req_complete(s->bus_num, s->addr, p,
-                                            p->status, p->actual_length);
-                break;
-            case EPIPE:
-            default:
-                p->status = USB_RET_STALL;
-                trace_usb_host_req_complete(s->bus_num, s->addr, p,
-                                            p->status, p->actual_length);
-            }
-            return;
-        }
-    } while (rem > 0);
-
-    p->status = USB_RET_ASYNC;
-}
-
-static int ctrl_error(void)
-{
-    if (errno == ETIMEDOUT) {
-        return USB_RET_NAK;
-    } else {
-        return USB_RET_STALL;
-    }
-}
-
-static void usb_host_set_address(USBHostDevice *s, int addr)
-{
-    trace_usb_host_set_address(s->bus_num, s->addr, addr);
-    s->dev.addr = addr;
-}
-
-static void usb_host_set_config(USBHostDevice *s, int config, USBPacket *p)
-{
-    int ret, first = 1;
-
-    trace_usb_host_set_config(s->bus_num, s->addr, config);
-
-    usb_host_release_interfaces(s);
-
-again:
-    ret = ioctl(s->fd, USBDEVFS_SETCONFIGURATION, &config);
-
-    DPRINTF("husb: ctrl set config %d ret %d errno %d\n", config, ret, errno);
-
-    if (ret < 0 && errno == EBUSY && first) {
-        /* happens if usb device is in use by host drivers */
-        int count = usb_linux_get_num_interfaces(s);
-        if (count > 0) {
-            DPRINTF("husb: busy -> disconnecting %d interfaces\n", count);
-            usb_host_disconnect_ifaces(s, count);
-            first = 0;
-            goto again;
-        }
-    }
-
-    if (ret < 0) {
-        p->status = ctrl_error();
-        return;
-    }
-    usb_host_claim_interfaces(s, config);
-    usb_linux_update_endp_table(s);
-}
-
-static void usb_host_set_interface(USBHostDevice *s, int iface, int alt,
-                                   USBPacket *p)
-{
-    struct usbdevfs_setinterface si;
-    int i, ret;
-
-    trace_usb_host_set_interface(s->bus_num, s->addr, iface, alt);
-
-    for (i = 1; i <= USB_MAX_ENDPOINTS; i++) {
-        if (is_isoc(s, USB_TOKEN_IN, i)) {
-            usb_host_stop_n_free_iso(s, USB_TOKEN_IN, i);
-        }
-        if (is_isoc(s, USB_TOKEN_OUT, i)) {
-            usb_host_stop_n_free_iso(s, USB_TOKEN_OUT, i);
-        }
-    }
-
-    if (iface >= USB_MAX_INTERFACES) {
-        p->status = USB_RET_STALL;
-        return;
-    }
-
-    si.interface  = iface;
-    si.altsetting = alt;
-    ret = ioctl(s->fd, USBDEVFS_SETINTERFACE, &si);
-
-    DPRINTF("husb: ctrl set iface %d altset %d ret %d errno %d\n",
-            iface, alt, ret, errno);
-
-    if (ret < 0) {
-        p->status = ctrl_error();
-        return;
-    }
-
-    s->dev.altsetting[iface] = alt;
-    usb_linux_update_endp_table(s);
-}
-
-static void usb_host_handle_control(USBDevice *dev, USBPacket *p,
-               int request, int value, int index, int length, uint8_t *data)
-{
-    USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
-    struct usbdevfs_urb *urb;
-    AsyncURB *aurb;
-    int ret;
-
-    /*
-     * Process certain standard device requests.
-     * These are infrequent and are processed synchronously.
-     */
-
-    /* Note request is (bRequestType << 8) | bRequest */
-    trace_usb_host_req_control(s->bus_num, s->addr, p, request, value, index);
-
-    switch (request) {
-    case DeviceOutRequest | USB_REQ_SET_ADDRESS:
-        usb_host_set_address(s, value);
-        trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status);
-        return;
-
-    case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
-        usb_host_set_config(s, value & 0xff, p);
-        trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status);
-        return;
-
-    case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
-        usb_host_set_interface(s, index, value, p);
-        trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status);
-        return;
-
-    case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
-        if (value == 0) { /* clear halt */
-            int pid = (index & USB_DIR_IN) ? USB_TOKEN_IN : USB_TOKEN_OUT;
-            ioctl(s->fd, USBDEVFS_CLEAR_HALT, &index);
-            clear_halt(s, pid, index & 0x0f);
-            trace_usb_host_req_emulated(s->bus_num, s->addr, p, 0);
-            return;
-        }
-    }
-
-    /* The rest are asynchronous */
-    if (length > sizeof(dev->data_buf)) {
-        fprintf(stderr, "husb: ctrl buffer too small (%d > %zu)\n",
-                length, sizeof(dev->data_buf));
-        p->status = USB_RET_STALL;
-        return;
-    }
-
-    aurb = async_alloc(s);
-    aurb->packet = p;
-
-    /*
-     * Setup ctrl transfer.
-     *
-     * s->ctrl is laid out such that data buffer immediately follows
-     * 'req' struct which is exactly what usbdevfs expects.
-     */
-    urb = &aurb->urb;
-
-    urb->type     = USBDEVFS_URB_TYPE_CONTROL;
-    urb->endpoint = p->ep->nr;
-
-    urb->buffer        = &dev->setup_buf;
-    urb->buffer_length = length + 8;
-
-    urb->usercontext = s;
-
-    trace_usb_host_urb_submit(s->bus_num, s->addr, aurb,
-                              urb->buffer_length, aurb->more);
-    ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb);
-
-    DPRINTF("husb: submit ctrl. len %u aurb %p\n", urb->buffer_length, aurb);
-
-    if (ret < 0) {
-        DPRINTF("husb: submit failed. errno %d\n", errno);
-        async_free(aurb);
-
-        switch(errno) {
-        case ETIMEDOUT:
-            p->status = USB_RET_NAK;
-            break;
-        case EPIPE:
-        default:
-            p->status = USB_RET_STALL;
-            break;
-        }
-        return;
-    }
-
-    p->status = USB_RET_ASYNC;
-}
-
-static void usb_linux_update_endp_table(USBHostDevice *s)
-{
-    static const char *tname[] = {
-        [USB_ENDPOINT_XFER_CONTROL] = "control",
-        [USB_ENDPOINT_XFER_ISOC]    = "isoc",
-        [USB_ENDPOINT_XFER_BULK]    = "bulk",
-        [USB_ENDPOINT_XFER_INT]     = "int",
-    };
-    uint8_t devep, type;
-    uint16_t mps, v, p;
-    int ep, pid;
-    unsigned int i, configuration = -1, interface = -1, altsetting = -1;
-    struct endp_data *epd;
-    USBDescriptor *d;
-    bool active = false;
-
-    usb_ep_reset(&s->dev);
-
-    for (i = 0;; i += d->bLength) {
-        if (i+2 >= s->descr_len) {
-            break;
-        }
-        d = (void *)(s->descr + i);
-        if (d->bLength < 2) {
-            trace_usb_host_parse_error(s->bus_num, s->addr,
-                                       "descriptor too short");
-            return;
-        }
-        if (i + d->bLength > s->descr_len) {
-            trace_usb_host_parse_error(s->bus_num, s->addr,
-                                       "descriptor too long");
-            return;
-        }
-        switch (d->bDescriptorType) {
-        case 0:
-            trace_usb_host_parse_error(s->bus_num, s->addr,
-                                       "invalid descriptor type");
-            return;
-        case USB_DT_DEVICE:
-            if (d->bLength < 0x12) {
-                trace_usb_host_parse_error(s->bus_num, s->addr,
-                                           "device descriptor too short");
-                return;
-            }
-            v = (d->u.device.idVendor_hi << 8) | d->u.device.idVendor_lo;
-            p = (d->u.device.idProduct_hi << 8) | d->u.device.idProduct_lo;
-            trace_usb_host_parse_device(s->bus_num, s->addr, v, p);
-            break;
-        case USB_DT_CONFIG:
-            if (d->bLength < 0x09) {
-                trace_usb_host_parse_error(s->bus_num, s->addr,
-                                           "config descriptor too short");
-                return;
-            }
-            configuration = d->u.config.bConfigurationValue;
-            active = (configuration == s->dev.configuration);
-            trace_usb_host_parse_config(s->bus_num, s->addr,
-                                        configuration, active);
-            break;
-        case USB_DT_INTERFACE:
-            if (d->bLength < 0x09) {
-                trace_usb_host_parse_error(s->bus_num, s->addr,
-                                           "interface descriptor too short");
-                return;
-            }
-            interface = d->u.interface.bInterfaceNumber;
-            altsetting = d->u.interface.bAlternateSetting;
-            active = (configuration == s->dev.configuration) &&
-                (altsetting == s->dev.altsetting[interface]);
-            trace_usb_host_parse_interface(s->bus_num, s->addr,
-                                           interface, altsetting, active);
-            break;
-        case USB_DT_ENDPOINT:
-            if (d->bLength < 0x07) {
-                trace_usb_host_parse_error(s->bus_num, s->addr,
-                                           "endpoint descriptor too short");
-                return;
-            }
-            devep = d->u.endpoint.bEndpointAddress;
-            pid = (devep & USB_DIR_IN) ? USB_TOKEN_IN : USB_TOKEN_OUT;
-            ep = devep & 0xf;
-            if (ep == 0) {
-                trace_usb_host_parse_error(s->bus_num, s->addr,
-                                           "invalid endpoint address");
-                return;
-            }
-
-            type = d->u.endpoint.bmAttributes & 0x3;
-            mps = d->u.endpoint.wMaxPacketSize_lo |
-                (d->u.endpoint.wMaxPacketSize_hi << 8);
-            trace_usb_host_parse_endpoint(s->bus_num, s->addr, ep,
-                                          (devep & USB_DIR_IN) ? "in" : "out",
-                                          tname[type], active);
-
-            if (active) {
-                usb_ep_set_max_packet_size(&s->dev, pid, ep, mps);
-                assert(usb_ep_get_type(&s->dev, pid, ep) ==
-                       USB_ENDPOINT_XFER_INVALID);
-                usb_ep_set_type(&s->dev, pid, ep, type);
-                usb_ep_set_ifnum(&s->dev, pid, ep, interface);
-                if ((s->options & (1 << USB_HOST_OPT_PIPELINE)) &&
-                    (type == USB_ENDPOINT_XFER_BULK) &&
-                    (pid == USB_TOKEN_OUT)) {
-                    usb_ep_set_pipeline(&s->dev, pid, ep, true);
-                }
-
-                epd = get_endp(s, pid, ep);
-                epd->halted = 0;
-            }
-
-            break;
-        default:
-            trace_usb_host_parse_unknown(s->bus_num, s->addr,
-                                         d->bLength, d->bDescriptorType);
-            break;
-        }
-    }
-}
-
-/*
- * Check if we can safely redirect a usb2 device to a usb1 virtual controller,
- * this function assumes this is safe, if:
- * 1) There are no isoc endpoints
- * 2) There are no interrupt endpoints with a max_packet_size > 64
- * Note bulk endpoints with a max_packet_size > 64 in theory also are not
- * usb1 compatible, but in practice this seems to work fine.
- */
-static int usb_linux_full_speed_compat(USBHostDevice *dev)
-{
-    int i, packet_size;
-
-    /*
-     * usb_linux_update_endp_table only registers info about ep in the current
-     * interface altsettings, so we need to parse the descriptors again.
-     */
-    for (i = 0; (i + 5) < dev->descr_len; i += dev->descr[i]) {
-        if (dev->descr[i + 1] == USB_DT_ENDPOINT) {
-            switch (dev->descr[i + 3] & 0x3) {
-            case 0x00: /* CONTROL */
-                break;
-            case 0x01: /* ISO */
-                return 0;
-            case 0x02: /* BULK */
-                break;
-            case 0x03: /* INTERRUPT */
-                packet_size = dev->descr[i + 4] + (dev->descr[i + 5] << 8);
-                if (packet_size > 64)
-                    return 0;
-                break;
-            }
-        }
-    }
-    return 1;
-}
-
-static int usb_host_open(USBHostDevice *dev, int bus_num,
-                         int addr, const char *port,
-                         const char *prod_name, int speed)
-{
-    int fd = -1, ret;
-
-    trace_usb_host_open_started(bus_num, addr);
-
-    if (dev->fd != -1) {
-        goto fail;
-    }
-
-    fd = usb_host_open_device(bus_num, addr);
-    if (fd < 0) {
-        goto fail;
-    }
-    DPRINTF("husb: opened %s\n", buf);
-
-    dev->bus_num = bus_num;
-    dev->addr = addr;
-    pstrcpy(dev->port, sizeof(dev->port), port);
-    dev->fd = fd;
-
-    /* read the device description */
-    dev->descr_len = read(fd, dev->descr, sizeof(dev->descr));
-    if (dev->descr_len <= 0) {
-        perror("husb: reading device data failed");
-        goto fail;
-    }
-
-#ifdef DEBUG
-    {
-        int x;
-        printf("=== begin dumping device descriptor data ===\n");
-        for (x = 0; x < dev->descr_len; x++) {
-            printf("%02x ", dev->descr[x]);
-        }
-        printf("\n=== end dumping device descriptor data ===\n");
-    }
-#endif
-
-
-    /* start unconfigured -- we'll wait for the guest to set a configuration */
-    if (!usb_host_claim_interfaces(dev, 0)) {
-        goto fail;
-    }
-
-    usb_ep_init(&dev->dev);
-    usb_linux_update_endp_table(dev);
-
-    if (speed == -1) {
-        struct usbdevfs_connectinfo ci;
-
-        ret = ioctl(fd, USBDEVFS_CONNECTINFO, &ci);
-        if (ret < 0) {
-            perror("usb_host_device_open: USBDEVFS_CONNECTINFO");
-            goto fail;
-        }
-
-        if (ci.slow) {
-            speed = USB_SPEED_LOW;
-        } else {
-            speed = USB_SPEED_HIGH;
-        }
-    }
-    dev->dev.speed = speed;
-    dev->dev.speedmask = (1 << speed);
-    if (dev->dev.speed == USB_SPEED_HIGH && usb_linux_full_speed_compat(dev)) {
-        dev->dev.speedmask |= USB_SPEED_MASK_FULL;
-    }
-
-    trace_usb_host_open_success(bus_num, addr);
-
-    if (!prod_name || prod_name[0] == '\0') {
-        snprintf(dev->dev.product_desc, sizeof(dev->dev.product_desc),
-                 "host:%d.%d", bus_num, addr);
-    } else {
-        pstrcpy(dev->dev.product_desc, sizeof(dev->dev.product_desc),
-                prod_name);
-    }
-
-    ret = usb_device_attach(&dev->dev);
-    if (ret) {
-        goto fail;
-    }
-
-    /* USB devio uses 'write' flag to check for async completions */
-    qemu_set_fd_handler(dev->fd, NULL, async_complete, dev);
-
-    return 0;
-
-fail:
-    trace_usb_host_open_failure(bus_num, addr);
-    if (dev->fd != -1) {
-        close(dev->fd);
-        dev->fd = -1;
-    }
-    return -1;
-}
-
-static int usb_host_close(USBHostDevice *dev)
-{
-    int i;
-
-    if (dev->fd == -1) {
-        return -1;
-    }
-
-    trace_usb_host_close(dev->bus_num, dev->addr);
-
-    qemu_set_fd_handler(dev->fd, NULL, NULL, NULL);
-    dev->closing = 1;
-    for (i = 1; i <= USB_MAX_ENDPOINTS; i++) {
-        if (is_isoc(dev, USB_TOKEN_IN, i)) {
-            usb_host_stop_n_free_iso(dev, USB_TOKEN_IN, i);
-        }
-        if (is_isoc(dev, USB_TOKEN_OUT, i)) {
-            usb_host_stop_n_free_iso(dev, USB_TOKEN_OUT, i);
-        }
-    }
-    async_complete(dev);
-    dev->closing = 0;
-    if (dev->dev.attached) {
-        usb_device_detach(&dev->dev);
-    }
-    usb_host_do_reset(dev);
-    close(dev->fd);
-    dev->fd = -1;
-    return 0;
-}
-
-static void usb_host_exit_notifier(struct Notifier *n, void *data)
-{
-    USBHostDevice *s = container_of(n, USBHostDevice, exit);
-
-    usb_host_release_port(s);
-    if (s->fd != -1) {
-        usb_host_do_reset(s);
-    }
-}
-
-/*
- * This is *NOT* about restoring state.  We have absolutely no idea
- * what state the host device is in at the moment and whenever it is
- * still present in the first place.  Attemping to contine where we
- * left off is impossible.
- *
- * What we are going to to to here is emulate a surprise removal of
- * the usb device passed through, then kick host scan so the device
- * will get re-attached (and re-initialized by the guest) in case it
- * is still present.
- *
- * As the device removal will change the state of other devices (usb
- * host controller, most likely interrupt controller too) we have to
- * wait with it until *all* vmstate is loaded.  Thus post_load just
- * kicks a bottom half which then does the actual work.
- */
-static void usb_host_post_load_bh(void *opaque)
-{
-    USBHostDevice *dev = opaque;
-
-    if (dev->fd != -1) {
-        usb_host_close(dev);
-    }
-    if (dev->dev.attached) {
-        usb_device_detach(&dev->dev);
-    }
-    usb_host_auto_check(NULL);
-}
-
-static int usb_host_post_load(void *opaque, int version_id)
-{
-    USBHostDevice *dev = opaque;
-
-    qemu_bh_schedule(dev->bh);
-    return 0;
-}
-
-static int usb_host_initfn(USBDevice *dev)
-{
-    USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
-
-    dev->flags |= (1 << USB_DEV_FLAG_IS_HOST);
-    dev->auto_attach = 0;
-    s->fd = -1;
-    s->hub_fd = -1;
-
-    QTAILQ_INSERT_TAIL(&hostdevs, s, next);
-    s->exit.notify = usb_host_exit_notifier;
-    qemu_add_exit_notifier(&s->exit);
-    s->bh = qemu_bh_new(usb_host_post_load_bh, s);
-    usb_host_auto_check(NULL);
-
-    if (s->match.bus_num != 0 && s->match.port != NULL) {
-        usb_host_claim_port(s);
-    }
-    add_boot_device_path(s->bootindex, &dev->qdev, NULL);
-    return 0;
-}
-
-static const VMStateDescription vmstate_usb_host = {
-    .name = DEVNAME,
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .post_load = usb_host_post_load,
-    .fields = (VMStateField[]) {
-        VMSTATE_USB_DEVICE(dev, USBHostDevice),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static Property usb_host_dev_properties[] = {
-    DEFINE_PROP_UINT32("hostbus",  USBHostDevice, match.bus_num,    0),
-    DEFINE_PROP_UINT32("hostaddr", USBHostDevice, match.addr,       0),
-    DEFINE_PROP_STRING("hostport", USBHostDevice, match.port),
-    DEFINE_PROP_HEX32("vendorid",  USBHostDevice, match.vendor_id,  0),
-    DEFINE_PROP_HEX32("productid", USBHostDevice, match.product_id, 0),
-    DEFINE_PROP_UINT32("isobufs",  USBHostDevice, iso_urb_count,    4),
-    DEFINE_PROP_INT32("bootindex", USBHostDevice, bootindex,        -1),
-    DEFINE_PROP_BIT("pipeline",    USBHostDevice, options,
-                    USB_HOST_OPT_PIPELINE, true),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void usb_host_class_initfn(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
-
-    uc->init           = usb_host_initfn;
-    uc->product_desc   = "USB Host Device";
-    uc->cancel_packet  = usb_host_async_cancel;
-    uc->handle_data    = usb_host_handle_data;
-    uc->handle_control = usb_host_handle_control;
-    uc->handle_reset   = usb_host_handle_reset;
-    uc->handle_destroy = usb_host_handle_destroy;
-    dc->vmsd = &vmstate_usb_host;
-    dc->props = usb_host_dev_properties;
-    set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
-}
-
-static const TypeInfo usb_host_dev_info = {
-    .name          = DEVNAME,
-    .parent        = TYPE_USB_DEVICE,
-    .instance_size = sizeof(USBHostDevice),
-    .class_init    = usb_host_class_initfn,
-};
-
-static void usb_host_register_types(void)
-{
-    type_register_static(&usb_host_dev_info);
-}
-
-type_init(usb_host_register_types)
-
-/*
- * Read sys file-system device file
- *
- * @line address of buffer to put file contents in
- * @line_size size of line
- * @device_file path to device file (printf format string)
- * @device_name device being opened (inserted into device_file)
- *
- * @return 0 failed, 1 succeeded ('line' contains data)
- */
-static int usb_host_read_file(char *line, size_t line_size,
-                              const char *device_file, const char *device_name)
-{
-    FILE *f;
-    int ret = 0;
-    char filename[PATH_MAX];
-
-    snprintf(filename, PATH_MAX, "/sys/bus/usb/devices/%s/%s", device_name,
-             device_file);
-    f = fopen(filename, "r");
-    if (f) {
-        ret = fgets(line, line_size, f) != NULL;
-        fclose(f);
-    }
-
-    return ret;
-}
-
-/*
- * Use /sys/bus/usb/devices/ directory to determine host's USB
- * devices.
- *
- * This code is based on Robert Schiele's original patches posted to
- * the Novell bug-tracker https://bugzilla.novell.com/show_bug.cgi?id=241950
- */
-static int usb_host_scan(void *opaque, USBScanFunc *func)
-{
-    DIR *dir = NULL;
-    char line[1024];
-    int bus_num, addr, speed, class_id, product_id, vendor_id;
-    int ret = 0;
-    char port[MAX_PORTLEN];
-    char product_name[512];
-    struct dirent *de;
-
-    dir = opendir("/sys/bus/usb/devices");
-    if (!dir) {
-        perror("husb: opendir /sys/bus/usb/devices");
-        fprintf(stderr, "husb: please make sure sysfs is mounted at /sys\n");
-        goto the_end;
-    }
-
-    while ((de = readdir(dir))) {
-        if (de->d_name[0] != '.' && !strchr(de->d_name, ':')) {
-            if (sscanf(de->d_name, "%d-%7[0-9.]", &bus_num, port) < 2) {
-                continue;
-            }
-
-            if (!usb_host_read_file(line, sizeof(line), "devnum", de->d_name)) {
-                goto the_end;
-            }
-            if (sscanf(line, "%d", &addr) != 1) {
-                goto the_end;
-            }
-            if (!usb_host_read_file(line, sizeof(line), "bDeviceClass",
-                                    de->d_name)) {
-                goto the_end;
-            }
-            if (sscanf(line, "%x", &class_id) != 1) {
-                goto the_end;
-            }
-
-            if (!usb_host_read_file(line, sizeof(line), "idVendor",
-                                    de->d_name)) {
-                goto the_end;
-            }
-            if (sscanf(line, "%x", &vendor_id) != 1) {
-                goto the_end;
-            }
-            if (!usb_host_read_file(line, sizeof(line), "idProduct",
-                                    de->d_name)) {
-                goto the_end;
-            }
-            if (sscanf(line, "%x", &product_id) != 1) {
-                goto the_end;
-            }
-            if (!usb_host_read_file(line, sizeof(line), "product",
-                                    de->d_name)) {
-                *product_name = 0;
-            } else {
-                if (strlen(line) > 0) {
-                    line[strlen(line) - 1] = '\0';
-                }
-                pstrcpy(product_name, sizeof(product_name), line);
-            }
-
-            if (!usb_host_read_file(line, sizeof(line), "speed", de->d_name)) {
-                goto the_end;
-            }
-            if (!strcmp(line, "5000\n")) {
-                speed = USB_SPEED_SUPER;
-            } else if (!strcmp(line, "480\n")) {
-                speed = USB_SPEED_HIGH;
-            } else if (!strcmp(line, "1.5\n")) {
-                speed = USB_SPEED_LOW;
-            } else {
-                speed = USB_SPEED_FULL;
-            }
-
-            ret = func(opaque, bus_num, addr, port, class_id, vendor_id,
-                       product_id, product_name, speed);
-            if (ret) {
-                goto the_end;
-            }
-        }
-    }
- the_end:
-    if (dir) {
-        closedir(dir);
-    }
-    return ret;
-}
-
-static QEMUTimer *usb_auto_timer;
-static VMChangeStateEntry *usb_vmstate;
-
-static int usb_host_auto_scan(void *opaque, int bus_num,
-                              int addr, const char *port,
-                              int class_id, int vendor_id, int product_id,
-                              const char *product_name, int speed)
-{
-    struct USBAutoFilter *f;
-    struct USBHostDevice *s;
-
-    /* Ignore hubs */
-    if (class_id == 9)
-        return 0;
-
-    QTAILQ_FOREACH(s, &hostdevs, next) {
-        f = &s->match;
-
-        if (f->bus_num > 0 && f->bus_num != bus_num) {
-            continue;
-        }
-        if (f->addr > 0 && f->addr != addr) {
-            continue;
-        }
-        if (f->port != NULL && strcmp(f->port, port) != 0) {
-            continue;
-        }
-
-        if (f->vendor_id > 0 && f->vendor_id != vendor_id) {
-            continue;
-        }
-
-        if (f->product_id > 0 && f->product_id != product_id) {
-            continue;
-        }
-        /* We got a match */
-        s->seen++;
-        if (s->errcount >= 3) {
-            return 0;
-        }
-
-        /* Already attached ? */
-        if (s->fd != -1) {
-            return 0;
-        }
-        DPRINTF("husb: auto open: bus_num %d addr %d\n", bus_num, addr);
-
-        if (usb_host_open(s, bus_num, addr, port, product_name, speed) < 0) {
-            s->errcount++;
-        }
-        break;
-    }
-
-    return 0;
-}
-
-static void usb_host_vm_state(void *unused, int running, RunState state)
-{
-    if (running) {
-        usb_host_auto_check(unused);
-    }
-}
-
-static void usb_host_auto_check(void *unused)
-{
-    struct USBHostDevice *s;
-    int unconnected = 0;
-
-    if (runstate_is_running()) {
-        usb_host_scan(NULL, usb_host_auto_scan);
-
-        QTAILQ_FOREACH(s, &hostdevs, next) {
-            if (s->fd == -1) {
-                unconnected++;
-            }
-            if (s->seen == 0) {
-                s->errcount = 0;
-            }
-            s->seen = 0;
-        }
-
-        if (unconnected == 0) {
-            /* nothing to watch */
-            if (usb_auto_timer) {
-                timer_del(usb_auto_timer);
-                trace_usb_host_auto_scan_disabled();
-            }
-            return;
-        }
-    }
-
-    if (!usb_vmstate) {
-        usb_vmstate = qemu_add_vm_change_state_handler(usb_host_vm_state, NULL);
-    }
-    if (!usb_auto_timer) {
-        usb_auto_timer = timer_new_ms(QEMU_CLOCK_REALTIME, usb_host_auto_check, NULL);
-        if (!usb_auto_timer) {
-            return;
-        }
-        trace_usb_host_auto_scan_enabled();
-    }
-    timer_mod(usb_auto_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 2000);
-}
-
-#ifndef CONFIG_USB_LIBUSB
-
-/**********************/
-/* USB host device info */
-
-struct usb_class_info {
-    int class;
-    const char *class_name;
-};
-
-static const struct usb_class_info usb_class_info[] = {
-    { USB_CLASS_AUDIO, "Audio"},
-    { USB_CLASS_COMM, "Communication"},
-    { USB_CLASS_HID, "HID"},
-    { USB_CLASS_HUB, "Hub" },
-    { USB_CLASS_PHYSICAL, "Physical" },
-    { USB_CLASS_PRINTER, "Printer" },
-    { USB_CLASS_MASS_STORAGE, "Storage" },
-    { USB_CLASS_CDC_DATA, "Data" },
-    { USB_CLASS_APP_SPEC, "Application Specific" },
-    { USB_CLASS_VENDOR_SPEC, "Vendor Specific" },
-    { USB_CLASS_STILL_IMAGE, "Still Image" },
-    { USB_CLASS_CSCID, "Smart Card" },
-    { USB_CLASS_CONTENT_SEC, "Content Security" },
-    { -1, NULL }
-};
-
-static const char *usb_class_str(uint8_t class)
-{
-    const struct usb_class_info *p;
-    for(p = usb_class_info; p->class != -1; p++) {
-        if (p->class == class) {
-            break;
-        }
-    }
-    return p->class_name;
-}
-
-static void usb_info_device(Monitor *mon, int bus_num,
-                            int addr, const char *port,
-                            int class_id, int vendor_id, int product_id,
-                            const char *product_name,
-                            int speed)
-{
-    const char *class_str, *speed_str;
-
-    switch(speed) {
-    case USB_SPEED_LOW:
-        speed_str = "1.5";
-        break;
-    case USB_SPEED_FULL:
-        speed_str = "12";
-        break;
-    case USB_SPEED_HIGH:
-        speed_str = "480";
-        break;
-    case USB_SPEED_SUPER:
-        speed_str = "5000";
-        break;
-    default:
-        speed_str = "?";
-        break;
-    }
-
-    monitor_printf(mon, "  Bus %d, Addr %d, Port %s, Speed %s Mb/s\n",
-                   bus_num, addr, port, speed_str);
-    class_str = usb_class_str(class_id);
-    if (class_str) {
-        monitor_printf(mon, "    %s:", class_str);
-    } else {
-        monitor_printf(mon, "    Class %02x:", class_id);
-    }
-    monitor_printf(mon, " USB device %04x:%04x", vendor_id, product_id);
-    if (product_name[0] != '\0') {
-        monitor_printf(mon, ", %s", product_name);
-    }
-    monitor_printf(mon, "\n");
-}
-
-static int usb_host_info_device(void *opaque, int bus_num, int addr,
-                                const char *path, int class_id,
-                                int vendor_id, int product_id,
-                                const char *product_name,
-                                int speed)
-{
-    Monitor *mon = opaque;
-
-    usb_info_device(mon, bus_num, addr, path, class_id, vendor_id, product_id,
-                    product_name, speed);
-    return 0;
-}
-
-static void dec2str(int val, char *str, size_t size)
-{
-    if (val == 0) {
-        snprintf(str, size, "*");
-    } else {
-        snprintf(str, size, "%d", val);
-    }
-}
-
-static void hex2str(int val, char *str, size_t size)
-{
-    if (val == 0) {
-        snprintf(str, size, "*");
-    } else {
-        snprintf(str, size, "%04x", val);
-    }
-}
-
-void usb_host_info(Monitor *mon, const QDict *qdict)
-{
-    struct USBAutoFilter *f;
-    struct USBHostDevice *s;
-
-    usb_host_scan(mon, usb_host_info_device);
-
-    if (QTAILQ_EMPTY(&hostdevs)) {
-        return;
-    }
-
-    monitor_printf(mon, "  Auto filters:\n");
-    QTAILQ_FOREACH(s, &hostdevs, next) {
-        char bus[10], addr[10], vid[10], pid[10];
-        f = &s->match;
-        dec2str(f->bus_num, bus, sizeof(bus));
-        dec2str(f->addr, addr, sizeof(addr));
-        hex2str(f->vendor_id, vid, sizeof(vid));
-        hex2str(f->product_id, pid, sizeof(pid));
-        monitor_printf(mon, "    Bus %s, Addr %s, Port %s, ID %s:%s\n",
-                       bus, addr, f->port ? f->port : "*", vid, pid);
-    }
-}
-
-#endif
index 728ec1aebf186bf0b9e97bceb47677cb93636b0c..f808550959cbb4b1488966ed307b9b97ce1d6d9c 100644 (file)
@@ -142,8 +142,9 @@ BlockDriver *bdrv_find_format(const char *format_name);
 BlockDriver *bdrv_find_whitelisted_format(const char *format_name,
                                           bool readonly);
 int bdrv_create(BlockDriver *drv, const char* filename,
-    QEMUOptionParameter *options);
-int bdrv_create_file(const char* filename, QEMUOptionParameter *options);
+    QEMUOptionParameter *options, Error **errp);
+int bdrv_create_file(const char* filename, QEMUOptionParameter *options,
+                     Error **errp);
 BlockDriverState *bdrv_new(const char *device_name);
 void bdrv_make_anon(BlockDriverState *bs);
 void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old);
@@ -151,10 +152,10 @@ void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top);
 int bdrv_parse_cache_flags(const char *mode, int *flags);
 int bdrv_parse_discard_flags(const char *mode, int *flags);
 int bdrv_file_open(BlockDriverState **pbs, const char *filename,
-                   QDict *options, int flags);
-int bdrv_open_backing_file(BlockDriverState *bs, QDict *options);
+                   QDict *options, int flags, Error **errp);
+int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp);
 int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
-              int flags, BlockDriver *drv);
+              int flags, BlockDriver *drv, Error **errp);
 BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
                                     BlockDriverState *bs, int flags);
 int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp);
@@ -241,6 +242,8 @@ typedef enum {
 
 int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix);
 
+int bdrv_amend_options(BlockDriverState *bs_new, QEMUOptionParameter *options);
+
 /* async block I/O */
 typedef void BlockDriverDirtyHandler(BlockDriverState *bs, int64_t sector,
                                      int sector_num);
index 7c35198ad7330a9916f3b92f41b89376d36d1407..3eeb6fe2a4433ebe12da765a8f353106f69bca5b 100644 (file)
@@ -80,15 +80,18 @@ struct BlockDriver {
     void (*bdrv_reopen_commit)(BDRVReopenState *reopen_state);
     void (*bdrv_reopen_abort)(BDRVReopenState *reopen_state);
 
-    int (*bdrv_open)(BlockDriverState *bs, QDict *options, int flags);
-    int (*bdrv_file_open)(BlockDriverState *bs, QDict *options, int flags);
+    int (*bdrv_open)(BlockDriverState *bs, QDict *options, int flags,
+                     Error **errp);
+    int (*bdrv_file_open)(BlockDriverState *bs, QDict *options, int flags,
+                          Error **errp);
     int (*bdrv_read)(BlockDriverState *bs, int64_t sector_num,
                      uint8_t *buf, int nb_sectors);
     int (*bdrv_write)(BlockDriverState *bs, int64_t sector_num,
                       const uint8_t *buf, int nb_sectors);
     void (*bdrv_close)(BlockDriverState *bs);
     void (*bdrv_rebind)(BlockDriverState *bs);
-    int (*bdrv_create)(const char *filename, QEMUOptionParameter *options);
+    int (*bdrv_create)(const char *filename, QEMUOptionParameter *options,
+                       Error **errp);
     int (*bdrv_set_key)(BlockDriverState *bs, const char *key);
     int (*bdrv_make_empty)(BlockDriverState *bs);
     /* aio */
@@ -150,7 +153,10 @@ struct BlockDriver {
                                 QEMUSnapshotInfo *sn_info);
     int (*bdrv_snapshot_goto)(BlockDriverState *bs,
                               const char *snapshot_id);
-    int (*bdrv_snapshot_delete)(BlockDriverState *bs, const char *snapshot_id);
+    int (*bdrv_snapshot_delete)(BlockDriverState *bs,
+                                const char *snapshot_id,
+                                const char *name,
+                                Error **errp);
     int (*bdrv_snapshot_list)(BlockDriverState *bs,
                               QEMUSnapshotInfo **psn_info);
     int (*bdrv_snapshot_load_tmp)(BlockDriverState *bs,
@@ -188,6 +194,9 @@ struct BlockDriver {
     int (*bdrv_check)(BlockDriverState* bs, BdrvCheckResult *result,
         BdrvCheckMode fix);
 
+    int (*bdrv_amend_options)(BlockDriverState *bs,
+        QEMUOptionParameter *options);
+
     void (*bdrv_debug_event)(BlockDriverState *bs, BlkDebugEvent event);
 
     /* TODO Better pass a option string/QDict/QemuOpts to add any rule? */
index eaf61f0326eb1b21df78b2df4e3978dff674876a..012bf226d3689e218b3461b35c8a8841ef8473f6 100644 (file)
@@ -26,6 +26,7 @@
 #define SNAPSHOT_H
 
 #include "qemu-common.h"
+#include "qapi/error.h"
 
 typedef struct QEMUSnapshotInfo {
     char id_str[128]; /* unique snapshot id */
@@ -40,12 +41,23 @@ typedef struct QEMUSnapshotInfo {
 
 int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info,
                        const char *name);
+bool bdrv_snapshot_find_by_id_and_name(BlockDriverState *bs,
+                                       const char *id,
+                                       const char *name,
+                                       QEMUSnapshotInfo *sn_info,
+                                       Error **errp);
 int bdrv_can_snapshot(BlockDriverState *bs);
 int bdrv_snapshot_create(BlockDriverState *bs,
                          QEMUSnapshotInfo *sn_info);
 int bdrv_snapshot_goto(BlockDriverState *bs,
                        const char *snapshot_id);
-int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id);
+int bdrv_snapshot_delete(BlockDriverState *bs,
+                         const char *snapshot_id,
+                         const char *name,
+                         Error **errp);
+void bdrv_snapshot_delete_by_id_or_name(BlockDriverState *bs,
+                                        const char *id_or_name,
+                                        Error **errp);
 int bdrv_snapshot_list(BlockDriverState *bs,
                        QEMUSnapshotInfo **psn_info);
 int bdrv_snapshot_load_tmp(BlockDriverState *bs,
index a407b50f4a0e9f343211460c0d12e55ffc199e0e..b6998f055a2dd51ed922bc17271805c4a9e63c11 100644 (file)
@@ -453,9 +453,7 @@ typedef struct RAMBlock {
      * Writes must take both locks.
      */
     QTAILQ_ENTRY(RAMBlock) next;
-#if defined(__linux__) && !defined(TARGET_S390X)
     int fd;
-#endif
 } RAMBlock;
 
 typedef struct RAMList {
index beb41491b4da1e54f00a8b4a81eaa4ebeb6e155b..77242e2d81f36394cb66d899064d3b1b86cb0697 100644 (file)
@@ -368,6 +368,8 @@ static inline uintptr_t tcg_getra_ext(uintptr_t ra)
 
 #if !defined(CONFIG_USER_ONLY)
 
+void phys_mem_set_alloc(void *(*alloc)(ram_addr_t));
+
 struct MemoryRegion *iotlb_to_region(hwaddr index);
 bool io_mem_read(struct MemoryRegion *mr, hwaddr addr,
                  uint64_t *pvalue, unsigned size);
index 3ddaf6aad5ff187bababced73a444835044f1c6c..4c0002beca0d61a02198fab722b1f981e014db7f 100644 (file)
@@ -53,6 +53,7 @@
 /* Vendors and devices.  Sort key: vendor first, device next. */
 
 #define PCI_VENDOR_ID_LSI_LOGIC          0x1000
+#define PCI_DEVICE_ID_LSI_53C810         0x0001
 #define PCI_DEVICE_ID_LSI_53C895A        0x0012
 #define PCI_DEVICE_ID_LSI_SAS1078        0x0060
 
index 6948bb91774a22d93dd59ecab62f061af803b093..50548361d09767155821af256a40c547a0cd31f7 100644 (file)
@@ -191,6 +191,9 @@ int64_t strtosz_suffix(const char *nptr, char **end, const char default_suffix);
 int64_t strtosz_suffix_unit(const char *nptr, char **end,
                             const char default_suffix, int64_t unit);
 
+/* used to print char* safely */
+#define STR_OR_NULL(str) ((str) ? (str) : "null")
+
 /* path.c */
 void init_paths(const char *prefix);
 const char *path(const char *pathname);
index 73c1ec5c40b77bec59989894fcff1f0d67d7d96a..3b25f27a7cc56dd9d8c8da703565df8725d81d71 100644 (file)
@@ -170,11 +170,6 @@ int kvm_cpu_exec(CPUState *cpu);
 
 #ifdef NEED_CPU_H
 
-#if !defined(CONFIG_USER_ONLY)
-void *kvm_ram_alloc(ram_addr_t size);
-void *kvm_arch_ram_alloc(ram_addr_t size);
-#endif
-
 void kvm_setup_guest_memory(void *start, size_t size);
 void kvm_flush_coalesced_mmio_buffer(void);
 
index b1aa05910253377ae25b2631066e9960bc8209fe..e2c6f58d9e7d050d704fa8f32d47940c0f0dfb37 100644 (file)
@@ -17,7 +17,9 @@ extern const char *bios_name;
 extern const char *qemu_name;
 extern uint8_t qemu_uuid[];
 int qemu_uuid_parse(const char *str, uint8_t *uuid);
+
 #define UUID_FMT "%02hhx%02hhx%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
+#define UUID_NONE "00000000-0000-0000-0000-000000000000"
 
 bool runstate_check(RunState state);
 void runstate_set(RunState new_state);
index 7630a7d6e369c39e5e386f17e4035ff8b243f28a..4478969ed2e0ee34b83e57b4e554242a36d26674 100644 (file)
--- a/kvm-all.c
+++ b/kvm-all.c
@@ -1836,19 +1836,6 @@ int kvm_has_intx_set_mask(void)
     return kvm_state->intx_set_mask;
 }
 
-void *kvm_ram_alloc(ram_addr_t size)
-{
-#ifdef TARGET_S390X
-    void *mem;
-
-    mem = kvm_arch_ram_alloc(size);
-    if (mem) {
-        return mem;
-    }
-#endif
-    return qemu_anon_ram_alloc(size);
-}
-
 void kvm_setup_guest_memory(void *start, size_t size)
 {
 #ifdef CONFIG_VALGRIND_H
index 1c38ea2b9397550b460acd46955f42bae5989d37..c3c9c28fbab786e0c50a5d1882d155680f0d46b9 100644 (file)
@@ -466,8 +466,7 @@ int main_loop_wait(int nonblocking)
     g_array_set_size(gpollfds, 0); /* reset for new iteration */
     /* XXX: separate device handlers from system ones */
 #ifdef CONFIG_SLIRP
-    slirp_update_timeout(&timeout);
-    slirp_pollfds_fill(gpollfds);
+    slirp_pollfds_fill(gpollfds, &timeout);
 #endif
     qemu_iohandler_fill(gpollfds);
 
index 2b2c8bce07d920cc3c836905877f733c63be373a..145eca8855cbbb798b164b2c6cae1471e24e194a 100644 (file)
   'data': { 'device': 'str', 'snapshot-file': 'str', '*format': 'str',
             '*mode': 'NewImageMode' } }
 
+##
+# @BlockdevSnapshotInternal
+#
+# @device: the name of the device to generate the snapshot from
+#
+# @name: the name of the internal snapshot to be created
+#
+# Notes: In transaction, if @name is empty, or any snapshot matching @name
+#        exists, the operation will fail. Only some image formats support it,
+#        for example, qcow2, rbd, and sheepdog.
+#
+# Since: 1.7
+##
+{ 'type': 'BlockdevSnapshotInternal',
+  'data': { 'device': 'str', 'name': 'str' } }
+
 ##
 # @DriveBackup
 #
   'data': {
        'blockdev-snapshot-sync': 'BlockdevSnapshot',
        'drive-backup': 'DriveBackup',
-       'abort': 'Abort'
+       'abort': 'Abort',
+       'blockdev-snapshot-internal-sync': 'BlockdevSnapshotInternal'
    } }
 
 ##
 { 'command': 'blockdev-snapshot-sync',
   'data': 'BlockdevSnapshot' }
 
+##
+# @blockdev-snapshot-internal-sync
+#
+# Synchronously take an internal snapshot of a block device, when the format
+# of the image used supports it.
+#
+# For the arguments, see the documentation of BlockdevSnapshotInternal.
+#
+# Returns: nothing on success
+#          If @device is not a valid block device, DeviceNotFound
+#          If any snapshot matching @name exists, or @name is empty,
+#          GenericError
+#          If the format of the image used does not support it,
+#          BlockFormatFeatureNotSupported
+#
+# Since 1.7
+##
+{ 'command': 'blockdev-snapshot-internal-sync',
+  'data': 'BlockdevSnapshotInternal' }
+
+##
+# @blockdev-snapshot-delete-internal-sync
+#
+# Synchronously delete an internal snapshot of a block device, when the format
+# of the image used support it. The snapshot is identified by name or id or
+# both. One of the name or id is required. Return SnapshotInfo for the
+# successfully deleted snapshot.
+#
+# @device: the name of the device to delete the snapshot from
+#
+# @id: optional the snapshot's ID to be deleted
+#
+# @name: optional the snapshot's name to be deleted
+#
+# Returns: SnapshotInfo on success
+#          If @device is not a valid block device, DeviceNotFound
+#          If snapshot not found, GenericError
+#          If the format of the image used does not support it,
+#          BlockFormatFeatureNotSupported
+#          If @id and @name are both not specified, GenericError
+#
+# Since 1.7
+##
+{ 'command': 'blockdev-snapshot-delete-internal-sync',
+  'data': { 'device': 'str', '*id': 'str', '*name': 'str'},
+  'returns': 'SnapshotInfo' }
+
 ##
 # @human-monitor-command:
 #
index 62594965bd162a79aa2014808171bdbc6c25b3c7..f7f5464b67635a7044f8725db60b37363be7675c 100644 (file)
@@ -1026,15 +1026,11 @@ static gboolean pty_chr_timer(gpointer opaque)
     struct CharDriverState *chr = opaque;
     PtyCharDriver *s = chr->opaque;
 
-    if (s->connected) {
-        goto out;
-    }
-
-    /* Next poll ... */
-    pty_chr_update_read_handler(chr);
-
-out:
     s->timer_tag = 0;
+    if (!s->connected) {
+        /* Next poll ... */
+        pty_chr_update_read_handler(chr);
+    }
     return FALSE;
 }
 
index 423430d3a032b78bc69cd0032108174b1f9173f7..470852100af1eb58e69f44d91cf29a11df7cff93 100644 (file)
@@ -30,15 +30,17 @@ static unsigned int pool_size;
 
 Coroutine *qemu_coroutine_create(CoroutineEntry *entry)
 {
-    Coroutine *co;
-
-    qemu_mutex_lock(&pool_lock);
-    co = QSLIST_FIRST(&pool);
-    if (co) {
-        QSLIST_REMOVE_HEAD(&pool, pool_next);
-        pool_size--;
+    Coroutine *co = NULL;
+
+    if (CONFIG_COROUTINE_POOL) {
+        qemu_mutex_lock(&pool_lock);
+        co = QSLIST_FIRST(&pool);
+        if (co) {
+            QSLIST_REMOVE_HEAD(&pool, pool_next);
+            pool_size--;
+        }
+        qemu_mutex_unlock(&pool_lock);
     }
-    qemu_mutex_unlock(&pool_lock);
 
     if (!co) {
         co = qemu_coroutine_new();
@@ -51,15 +53,17 @@ Coroutine *qemu_coroutine_create(CoroutineEntry *entry)
 
 static void coroutine_delete(Coroutine *co)
 {
-    qemu_mutex_lock(&pool_lock);
-    if (pool_size < POOL_MAX_SIZE) {
-        QSLIST_INSERT_HEAD(&pool, co, pool_next);
-        co->caller = NULL;
-        pool_size++;
+    if (CONFIG_COROUTINE_POOL) {
+        qemu_mutex_lock(&pool_lock);
+        if (pool_size < POOL_MAX_SIZE) {
+            QSLIST_INSERT_HEAD(&pool, co, pool_next);
+            co->caller = NULL;
+            pool_size++;
+            qemu_mutex_unlock(&pool_lock);
+            return;
+        }
         qemu_mutex_unlock(&pool_lock);
-        return;
     }
-    qemu_mutex_unlock(&pool_lock);
 
     qemu_coroutine_delete(co);
 }
index 0c36e5968ff5e3952e42795b2252d0c2158a9d74..da1d965f3e263b95fcf20f58aae7740ce9dd55c3 100644 (file)
@@ -67,5 +67,11 @@ DEF("resize", img_resize,
     "resize [-q] filename [+ | -]size")
 STEXI
 @item resize [-q] @var{filename} [+ | -]@var{size}
+ETEXI
+
+DEF("amend", img_amend,
+    "amend [-q] [-f fmt] -o options filename")
+STEXI
+@item amend [-q] [-f @var{fmt}] -o @var{options} @var{filename}
 @end table
 ETEXI
index 3e5e388d1ca891fa07c619bfff52c5363f55cf66..926f0a0febc425563d8d1ba4699abbae8060aa22 100644 (file)
@@ -266,6 +266,7 @@ static BlockDriverState *bdrv_new_open(const char *filename,
     BlockDriverState *bs;
     BlockDriver *drv;
     char password[256];
+    Error *local_err = NULL;
     int ret;
 
     bs = bdrv_new("image");
@@ -280,9 +281,11 @@ static BlockDriverState *bdrv_new_open(const char *filename,
         drv = NULL;
     }
 
-    ret = bdrv_open(bs, filename, NULL, flags, drv);
+    ret = bdrv_open(bs, filename, NULL, flags, drv, &local_err);
     if (ret < 0) {
-        error_report("Could not open '%s': %s", filename, strerror(-ret));
+        error_report("Could not open '%s': %s", filename,
+                     error_get_pretty(local_err));
+        error_free(local_err);
         goto fail;
     }
 
@@ -409,7 +412,7 @@ static int img_create(int argc, char **argv)
     bdrv_img_create(filename, fmt, base_filename, base_fmt,
                     options, img_size, BDRV_O_FLAGS, &local_err, quiet);
     if (error_is_set(&local_err)) {
-        error_report("%s", error_get_pretty(local_err));
+        error_report("%s: %s", filename, error_get_pretty(local_err));
         error_free(local_err);
         return 1;
     }
@@ -1136,6 +1139,7 @@ static int img_convert(int argc, char **argv)
     float local_progress = 0;
     int min_sparse = 8; /* Need at least 4k of zeros for sparse detection */
     bool quiet = false;
+    Error *local_err = NULL;
 
     fmt = NULL;
     out_fmt = "raw";
@@ -1338,18 +1342,11 @@ static int img_convert(int argc, char **argv)
 
     if (!skip_create) {
         /* Create the new image */
-        ret = bdrv_create(drv, out_filename, param);
+        ret = bdrv_create(drv, out_filename, param, &local_err);
         if (ret < 0) {
-            if (ret == -ENOTSUP) {
-                error_report("Formatting not supported for file format '%s'",
-                             out_fmt);
-            } else if (ret == -EFBIG) {
-                error_report("The image size is too large for file format '%s'",
-                             out_fmt);
-            } else {
-                error_report("%s: error while converting %s: %s",
-                             out_filename, out_fmt, strerror(-ret));
-            }
+            error_report("%s: error while converting %s: %s",
+                         out_filename, out_fmt, error_get_pretty(local_err));
+            error_free(local_err);
             goto out;
         }
     }
@@ -1842,7 +1839,7 @@ static void dump_map_entry(OutputFormat output_format, MapEntry *e,
                (e->flags & BDRV_BLOCK_ZERO) ? "true" : "false",
                (e->flags & BDRV_BLOCK_DATA) ? "true" : "false");
         if (e->flags & BDRV_BLOCK_OFFSET_VALID) {
-            printf(", 'offset': %"PRId64"", e->offset);
+            printf(", \"offset\": %"PRId64"", e->offset);
         }
         putchar('}');
 
@@ -2006,6 +2003,7 @@ static int img_snapshot(int argc, char **argv)
     int action = 0;
     qemu_timeval tv;
     bool quiet = false;
+    Error *err = NULL;
 
     bdrv_oflags = BDRV_O_FLAGS | BDRV_O_RDWR;
     /* Parse commandline parameters */
@@ -2098,10 +2096,12 @@ static int img_snapshot(int argc, char **argv)
         break;
 
     case SNAPSHOT_DELETE:
-        ret = bdrv_snapshot_delete(bs, snapshot_name);
-        if (ret) {
-            error_report("Could not delete snapshot '%s': %d (%s)",
-                snapshot_name, ret, strerror(-ret));
+        bdrv_snapshot_delete_by_id_or_name(bs, snapshot_name, &err);
+        if (error_is_set(&err)) {
+            error_report("Could not delete snapshot '%s': (%s)",
+                         snapshot_name, error_get_pretty(err));
+            error_free(err);
+            ret = 1;
         }
         break;
     }
@@ -2124,6 +2124,7 @@ static int img_rebase(int argc, char **argv)
     int unsafe = 0;
     int progress = 0;
     bool quiet = false;
+    Error *local_err = NULL;
 
     /* Parse commandline parameters */
     fmt = NULL;
@@ -2227,18 +2228,21 @@ static int img_rebase(int argc, char **argv)
         bs_old_backing = bdrv_new("old_backing");
         bdrv_get_backing_filename(bs, backing_name, sizeof(backing_name));
         ret = bdrv_open(bs_old_backing, backing_name, NULL, BDRV_O_FLAGS,
-                        old_backing_drv);
+                        old_backing_drv, &local_err);
         if (ret) {
-            error_report("Could not open old backing file '%s'", backing_name);
+            error_report("Could not open old backing file '%s': %s",
+                         backing_name, error_get_pretty(local_err));
+            error_free(local_err);
             goto out;
         }
         if (out_baseimg[0]) {
             bs_new_backing = bdrv_new("new_backing");
             ret = bdrv_open(bs_new_backing, out_baseimg, NULL, BDRV_O_FLAGS,
-                        new_backing_drv);
+                        new_backing_drv, &local_err);
             if (ret) {
-                error_report("Could not open new backing file '%s'",
-                             out_baseimg);
+                error_report("Could not open new backing file '%s': %s",
+                             out_baseimg, error_get_pretty(local_err));
+                error_free(local_err);
                 goto out;
             }
         }
@@ -2525,6 +2529,90 @@ out:
     return 0;
 }
 
+static int img_amend(int argc, char **argv)
+{
+    int c, ret = 0;
+    char *options = NULL;
+    QEMUOptionParameter *create_options = NULL, *options_param = NULL;
+    const char *fmt = NULL, *filename;
+    bool quiet = false;
+    BlockDriverState *bs = NULL;
+
+    for (;;) {
+        c = getopt(argc, argv, "hqf:o:");
+        if (c == -1) {
+            break;
+        }
+
+        switch (c) {
+            case 'h':
+            case '?':
+                help();
+                break;
+            case 'o':
+                options = optarg;
+                break;
+            case 'f':
+                fmt = optarg;
+                break;
+            case 'q':
+                quiet = true;
+                break;
+        }
+    }
+
+    if (optind != argc - 1) {
+        help();
+    }
+
+    if (!options) {
+        help();
+    }
+
+    filename = argv[argc - 1];
+
+    bs = bdrv_new_open(filename, fmt, BDRV_O_FLAGS | BDRV_O_RDWR, true, quiet);
+    if (!bs) {
+        error_report("Could not open image '%s'", filename);
+        ret = -1;
+        goto out;
+    }
+
+    fmt = bs->drv->format_name;
+
+    if (is_help_option(options)) {
+        ret = print_block_option_help(filename, fmt);
+        goto out;
+    }
+
+    create_options = append_option_parameters(create_options,
+            bs->drv->create_options);
+    options_param = parse_option_parameters(options, create_options,
+            options_param);
+    if (options_param == NULL) {
+        error_report("Invalid options for file format '%s'", fmt);
+        ret = -1;
+        goto out;
+    }
+
+    ret = bdrv_amend_options(bs, options_param);
+    if (ret < 0) {
+        error_report("Error while amending options: %s", strerror(-ret));
+        goto out;
+    }
+
+out:
+    if (bs) {
+        bdrv_unref(bs);
+    }
+    free_option_parameters(create_options);
+    free_option_parameters(options_param);
+    if (ret) {
+        return 1;
+    }
+    return 0;
+}
+
 static const img_cmd_t img_cmds[] = {
 #define DEF(option, callback, arg_string)        \
     { option, callback },
index 43ee4eb5c47a5791c7cc347aee117fa724ada1a7..768054e9008d93bf0e8420bfbf02516dd8eab0f7 100644 (file)
@@ -350,6 +350,11 @@ sizes accordingly.  Failure to do so will result in data loss!
 After using this command to grow a disk image, you must use file system and
 partitioning tools inside the VM to actually begin using the new space on the
 device.
+
+@item amend [-f @var{fmt}] -o @var{options} @var{filename}
+
+Amends the image format specific @var{options} for the image file
+@var{filename}. Not all file formats support this operation.
 @end table
 @c man end
 
index 71f4ff1302107a28e912393dcd8d25542265c164..f4b8efccebd44b284185110097695008ddb83cc4 100644 (file)
--- a/qemu-io.c
+++ b/qemu-io.c
@@ -46,21 +46,27 @@ static const cmdinfo_t close_cmd = {
 
 static int openfile(char *name, int flags, int growable)
 {
+    Error *local_err = NULL;
+
     if (qemuio_bs) {
         fprintf(stderr, "file open already, try 'help close'\n");
         return 1;
     }
 
     if (growable) {
-        if (bdrv_file_open(&qemuio_bs, name, NULL, flags)) {
-            fprintf(stderr, "%s: can't open device %s\n", progname, name);
+        if (bdrv_file_open(&qemuio_bs, name, NULL, flags, &local_err)) {
+            fprintf(stderr, "%s: can't open device %s: %s\n", progname, name,
+                    error_get_pretty(local_err));
+            error_free(local_err);
             return 1;
         }
     } else {
         qemuio_bs = bdrv_new("hda");
 
-        if (bdrv_open(qemuio_bs, name, NULL, flags, NULL) < 0) {
-            fprintf(stderr, "%s: can't open device %s\n", progname, name);
+        if (bdrv_open(qemuio_bs, name, NULL, flags, NULL, &local_err) < 0) {
+            fprintf(stderr, "%s: can't open device %s: %s\n", progname, name,
+                    error_get_pretty(local_err));
+            error_free(local_err);
             bdrv_unref(qemuio_bs);
             qemuio_bs = NULL;
             return 1;
index f044546c28469bc8f289cb3b72e1d50b10c1a57b..c26c98ef1d97ae7b8a2b375572cdb2ba1e71213f 100644 (file)
@@ -355,6 +355,7 @@ int main(int argc, char **argv)
 #endif
     pthread_t client_thread;
     const char *fmt = NULL;
+    Error *local_err = NULL;
 
     /* The client thread uses SIGTERM to interrupt the server.  A signal
      * handler ensures that "qemu-nbd -v -c" exits with a nice status code.
@@ -573,10 +574,11 @@ int main(int argc, char **argv)
 
     bs = bdrv_new("hda");
     srcpath = argv[optind];
-    ret = bdrv_open(bs, srcpath, NULL, flags, drv);
+    ret = bdrv_open(bs, srcpath, NULL, flags, drv, &local_err);
     if (ret < 0) {
         errno = -ret;
-        err(EXIT_FAILURE, "Failed to bdrv_open '%s'", argv[optind]);
+        err(EXIT_FAILURE, "Failed to bdrv_open '%s': %s", argv[optind],
+            error_get_pretty(local_err));
     }
 
     fd_size = bdrv_getlength(bs);
index 008cad95a2b9bed8220fa1eec053bed2dfab321f..b17c46e0b1d108c816a2459999370ea65970e40c 100644 (file)
@@ -1001,14 +1001,15 @@ SQMP
 transaction
 -----------
 
-Atomically operate on one or more block devices.  The only supported
-operation for now is snapshotting.  If there is any failure performing
-any of the operations, all snapshots for the group are abandoned, and
-the original disks pre-snapshot attempt are used.
+Atomically operate on one or more block devices.  The only supported operations
+for now are drive-backup, internal and external snapshotting.  A list of
+dictionaries is accepted, that contains the actions to be performed.
+If there is any failure performing any of the operations, all operations
+for the group are abandoned.
 
-A list of dictionaries is accepted, that contains the actions to be performed.
-For snapshots this is the device, the file to use for the new snapshot,
-and the format.  The default format, if not specified, is qcow2.
+For external snapshots, the dictionary contains the device, the file to use for
+the new snapshot, and the format.  The default format, if not specified, is
+qcow2.
 
 Each new snapshot defaults to being created by QEMU (wiping any
 contents if the file already exists), but it is also possible to reuse
@@ -1017,6 +1018,17 @@ the new image file has the same contents as the current one; QEMU cannot
 perform any meaningful check.  Typically this is achieved by using the
 current image file as the backing file for the new image.
 
+On failure, the original disks pre-snapshot attempt will be used.
+
+For internal snapshots, the dictionary contains the device and the snapshot's
+name.  If an internal snapshot matching name already exists, the request will
+be rejected.  Only some image formats support it, for example, qcow2, rbd,
+and sheepdog.
+
+On failure, qemu will try delete the newly created internal snapshot in the
+transaction.  When an I/O error occurs during deletion, the user needs to fix
+it later with qemu-img or other command.
+
 Arguments:
 
 actions array:
@@ -1029,6 +1041,9 @@ actions array:
       - "format": format of new image (json-string, optional)
       - "mode": whether and how QEMU should create the snapshot file
         (NewImageMode, optional, default "absolute-paths")
+      When "type" is "blockdev-snapshot-internal-sync":
+      - "device": device name to snapshot (json-string)
+      - "name": name of the new snapshot (json-string)
 
 Example:
 
@@ -1040,7 +1055,10 @@ Example:
          { 'type': 'blockdev-snapshot-sync', 'data' : { "device": "ide-hd1",
                                          "snapshot-file": "/some/place/my-image2",
                                          "mode": "existing",
-                                         "format": "qcow2" } } ] } }
+                                         "format": "qcow2" } },
+         { 'type': 'blockdev-snapshot-internal-sync', 'data' : {
+                                         "device": "ide-hd2",
+                                         "name": "snapshot0" } } ] } }
 <- { "return": {} }
 
 EQMP
@@ -1077,6 +1095,76 @@ Example:
                                                         "format": "qcow2" } }
 <- { "return": {} }
 
+EQMP
+
+    {
+        .name       = "blockdev-snapshot-internal-sync",
+        .args_type  = "device:B,name:s",
+        .mhandler.cmd_new = qmp_marshal_input_blockdev_snapshot_internal_sync,
+    },
+
+SQMP
+blockdev-snapshot-internal-sync
+-------------------------------
+
+Synchronously take an internal snapshot of a block device when the format of
+image used supports it.  If the name is an empty string, or a snapshot with
+name already exists, the operation will fail.
+
+Arguments:
+
+- "device": device name to snapshot (json-string)
+- "name": name of the new snapshot (json-string)
+
+Example:
+
+-> { "execute": "blockdev-snapshot-internal-sync",
+                "arguments": { "device": "ide-hd0",
+                               "name": "snapshot0" }
+   }
+<- { "return": {} }
+
+EQMP
+
+    {
+        .name       = "blockdev-snapshot-delete-internal-sync",
+        .args_type  = "device:B,id:s?,name:s?",
+        .mhandler.cmd_new =
+                      qmp_marshal_input_blockdev_snapshot_delete_internal_sync,
+    },
+
+SQMP
+blockdev-snapshot-delete-internal-sync
+--------------------------------------
+
+Synchronously delete an internal snapshot of a block device when the format of
+image used supports it.  The snapshot is identified by name or id or both.  One
+of name or id is required.  If the snapshot is not found, the operation will
+fail.
+
+Arguments:
+
+- "device": device name (json-string)
+- "id": ID of the snapshot (json-string, optional)
+- "name": name of the snapshot (json-string, optional)
+
+Example:
+
+-> { "execute": "blockdev-snapshot-delete-internal-sync",
+                "arguments": { "device": "ide-hd0",
+                               "name": "snapshot0" }
+   }
+<- { "return": {
+                   "id": "1",
+                   "name": "snapshot0",
+                   "vm-state-size": 0,
+                   "date-sec": 1000012,
+                   "date-nsec": 10,
+                   "vm-clock-sec": 100,
+                   "vm-clock-nsec": 20
+     }
+   }
+
 EQMP
 
     {
index c536aa4986bd5381d53f363577c4cf4a5e3465b4..4a3c819fcdda5a4ded652c510f6178a03f9324d9 100644 (file)
--- a/savevm.c
+++ b/savevm.c
@@ -2325,18 +2325,21 @@ static int del_existing_snapshots(Monitor *mon, const char *name)
 {
     BlockDriverState *bs;
     QEMUSnapshotInfo sn1, *snapshot = &sn1;
-    int ret;
+    Error *err = NULL;
 
     bs = NULL;
     while ((bs = bdrv_next(bs))) {
         if (bdrv_can_snapshot(bs) &&
             bdrv_snapshot_find(bs, snapshot, name) >= 0)
         {
-            ret = bdrv_snapshot_delete(bs, name);
-            if (ret < 0) {
+            bdrv_snapshot_delete_by_id_or_name(bs, name, &err);
+            if (error_is_set(&err)) {
                 monitor_printf(mon,
-                               "Error while deleting snapshot on '%s'\n",
-                               bdrv_get_device_name(bs));
+                               "Error while deleting snapshot on device '%s':"
+                               " %s\n",
+                               bdrv_get_device_name(bs),
+                               error_get_pretty(err));
+                error_free(err);
                 return -1;
             }
         }
@@ -2550,7 +2553,7 @@ int load_vmstate(const char *name)
 void do_delvm(Monitor *mon, const QDict *qdict)
 {
     BlockDriverState *bs, *bs1;
-    int ret;
+    Error *err = NULL;
     const char *name = qdict_get_str(qdict, "name");
 
     bs = find_vmstate_bs();
@@ -2562,15 +2565,14 @@ void do_delvm(Monitor *mon, const QDict *qdict)
     bs1 = NULL;
     while ((bs1 = bdrv_next(bs1))) {
         if (bdrv_can_snapshot(bs1)) {
-            ret = bdrv_snapshot_delete(bs1, name);
-            if (ret < 0) {
-                if (ret == -ENOTSUP)
-                    monitor_printf(mon,
-                                   "Snapshots not supported on device '%s'\n",
-                                   bdrv_get_device_name(bs1));
-                else
-                    monitor_printf(mon, "Error %d while deleting snapshot on "
-                                   "'%s'\n", ret, bdrv_get_device_name(bs1));
+            bdrv_snapshot_delete_by_id_or_name(bs, name, &err);
+            if (error_is_set(&err)) {
+                monitor_printf(mon,
+                               "Error while deleting snapshot on device '%s':"
+                               " %s\n",
+                               bdrv_get_device_name(bs),
+                               error_get_pretty(err));
+                error_free(err);
             }
         }
     }
diff --git a/scripts/qmp/qemu-ga-client b/scripts/qmp/qemu-ga-client
new file mode 100755 (executable)
index 0000000..b5f7e7c
--- /dev/null
@@ -0,0 +1,301 @@
+#!/usr/bin/python
+
+# QEMU Guest Agent Client
+#
+# Copyright (C) 2012 Ryota Ozaki <ozaki.ryota@gmail.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2.  See
+# the COPYING file in the top-level directory.
+#
+# Usage:
+#
+# Start QEMU with:
+#
+# # qemu [...] -chardev socket,path=/tmp/qga.sock,server,nowait,id=qga0 \
+#   -device virtio-serial -device virtserialport,chardev=qga0,name=org.qemu.guest_agent.0
+#
+# Run the script:
+#
+# $ qemu-ga-client --address=/tmp/qga.sock <command> [args...]
+#
+# or
+#
+# $ export QGA_CLIENT_ADDRESS=/tmp/qga.sock
+# $ qemu-ga-client <command> [args...]
+#
+# For example:
+#
+# $ qemu-ga-client cat /etc/resolv.conf
+# # Generated by NetworkManager
+# nameserver 10.0.2.3
+# $ qemu-ga-client fsfreeze status
+# thawed
+# $ qemu-ga-client fsfreeze freeze
+# 2 filesystems frozen
+#
+# See also: http://wiki.qemu.org/Features/QAPI/GuestAgent
+#
+
+import base64
+import random
+
+import qmp
+
+
+class QemuGuestAgent(qmp.QEMUMonitorProtocol):
+    def __getattr__(self, name):
+        def wrapper(**kwds):
+            return self.command('guest-' + name.replace('_', '-'), **kwds)
+        return wrapper
+
+
+class QemuGuestAgentClient:
+    error = QemuGuestAgent.error
+
+    def __init__(self, address):
+        self.qga = QemuGuestAgent(address)
+        self.qga.connect(negotiate=False)
+
+    def sync(self, timeout=3):
+        # Avoid being blocked forever
+        if not self.ping(timeout):
+            raise EnvironmentError('Agent seems not alive')
+        uid = random.randint(0, (1 << 32) - 1)
+        while True:
+            ret = self.qga.sync(id=uid)
+            if isinstance(ret, int) and int(ret) == uid:
+                break
+
+    def __file_read_all(self, handle):
+        eof = False
+        data = ''
+        while not eof:
+            ret = self.qga.file_read(handle=handle, count=1024)
+            _data = base64.b64decode(ret['buf-b64'])
+            data += _data
+            eof = ret['eof']
+        return data
+
+    def read(self, path):
+        handle = self.qga.file_open(path=path)
+        try:
+            data = self.__file_read_all(handle)
+        finally:
+            self.qga.file_close(handle=handle)
+        return data
+
+    def info(self):
+        info = self.qga.info()
+
+        msgs = []
+        msgs.append('version: ' + info['version'])
+        msgs.append('supported_commands:')
+        enabled = [c['name'] for c in info['supported_commands'] if c['enabled']]
+        msgs.append('\tenabled: ' + ', '.join(enabled))
+        disabled = [c['name'] for c in info['supported_commands'] if not c['enabled']]
+        msgs.append('\tdisabled: ' + ', '.join(disabled))
+
+        return '\n'.join(msgs)
+
+    def __gen_ipv4_netmask(self, prefixlen):
+        mask = int('1' * prefixlen + '0' * (32 - prefixlen), 2)
+        return '.'.join([str(mask >> 24),
+                         str((mask >> 16) & 0xff),
+                         str((mask >> 8) & 0xff),
+                         str(mask & 0xff)])
+
+    def ifconfig(self):
+        nifs = self.qga.network_get_interfaces()
+
+        msgs = []
+        for nif in nifs:
+            msgs.append(nif['name'] + ':')
+            if 'ip-addresses' in nif:
+                for ipaddr in nif['ip-addresses']:
+                    if ipaddr['ip-address-type'] == 'ipv4':
+                        addr = ipaddr['ip-address']
+                        mask = self.__gen_ipv4_netmask(int(ipaddr['prefix']))
+                        msgs.append("\tinet %s  netmask %s" % (addr, mask))
+                    elif ipaddr['ip-address-type'] == 'ipv6':
+                        addr = ipaddr['ip-address']
+                        prefix = ipaddr['prefix']
+                        msgs.append("\tinet6 %s  prefixlen %s" % (addr, prefix))
+            if nif['hardware-address'] != '00:00:00:00:00:00':
+                msgs.append("\tether " + nif['hardware-address'])
+
+        return '\n'.join(msgs)
+
+    def ping(self, timeout):
+        self.qga.settimeout(timeout)
+        try:
+            self.qga.ping()
+        except self.qga.timeout:
+            return False
+        return True
+
+    def fsfreeze(self, cmd):
+        if cmd not in ['status', 'freeze', 'thaw']:
+            raise StandardError('Invalid command: ' + cmd)
+
+        return getattr(self.qga, 'fsfreeze' + '_' + cmd)()
+
+    def fstrim(self, minimum=0):
+        return getattr(self.qga, 'fstrim')(minimum=minimum)
+
+    def suspend(self, mode):
+        if mode not in ['disk', 'ram', 'hybrid']:
+            raise StandardError('Invalid mode: ' + mode)
+
+        try:
+            getattr(self.qga, 'suspend' + '_' + mode)()
+            # On error exception will raise
+        except self.qga.timeout:
+            # On success command will timed out
+            return
+
+    def shutdown(self, mode='powerdown'):
+        if mode not in ['powerdown', 'halt', 'reboot']:
+            raise StandardError('Invalid mode: ' + mode)
+
+        try:
+            self.qga.shutdown(mode=mode)
+        except self.qga.timeout:
+            return
+
+
+def _cmd_cat(client, args):
+    if len(args) != 1:
+        print('Invalid argument')
+        print('Usage: cat <file>')
+        sys.exit(1)
+    print(client.read(args[0]))
+
+
+def _cmd_fsfreeze(client, args):
+    usage = 'Usage: fsfreeze status|freeze|thaw'
+    if len(args) != 1:
+        print('Invalid argument')
+        print(usage)
+        sys.exit(1)
+    if args[0] not in ['status', 'freeze', 'thaw']:
+        print('Invalid command: ' + args[0])
+        print(usage)
+        sys.exit(1)
+    cmd = args[0]
+    ret = client.fsfreeze(cmd)
+    if cmd == 'status':
+        print(ret)
+    elif cmd == 'freeze':
+        print("%d filesystems frozen" % ret)
+    else:
+        print("%d filesystems thawed" % ret)
+
+
+def _cmd_fstrim(client, args):
+    if len(args) == 0:
+        minimum = 0
+    else:
+        minimum = int(args[0])
+    print(client.fstrim(minimum))
+
+
+def _cmd_ifconfig(client, args):
+    print(client.ifconfig())
+
+
+def _cmd_info(client, args):
+    print(client.info())
+
+
+def _cmd_ping(client, args):
+    if len(args) == 0:
+        timeout = 3
+    else:
+        timeout = float(args[0])
+    alive = client.ping(timeout)
+    if not alive:
+        print("Not responded in %s sec" % args[0])
+        sys.exit(1)
+
+
+def _cmd_suspend(client, args):
+    usage = 'Usage: suspend disk|ram|hybrid'
+    if len(args) != 1:
+        print('Less argument')
+        print(usage)
+        sys.exit(1)
+    if args[0] not in ['disk', 'ram', 'hybrid']:
+        print('Invalid command: ' + args[0])
+        print(usage)
+        sys.exit(1)
+    client.suspend(args[0])
+
+
+def _cmd_shutdown(client, args):
+    client.shutdown()
+_cmd_powerdown = _cmd_shutdown
+
+
+def _cmd_halt(client, args):
+    client.shutdown('halt')
+
+
+def _cmd_reboot(client, args):
+    client.shutdown('reboot')
+
+
+commands = [m.replace('_cmd_', '') for m in dir() if '_cmd_' in m]
+
+
+def main(address, cmd, args):
+    if not os.path.exists(address):
+        print('%s not found' % address)
+        sys.exit(1)
+
+    if cmd not in commands:
+        print('Invalid command: ' + cmd)
+        print('Available commands: ' + ', '.join(commands))
+        sys.exit(1)
+
+    try:
+        client = QemuGuestAgentClient(address)
+    except QemuGuestAgent.error, e:
+        import errno
+
+        print(e)
+        if e.errno == errno.ECONNREFUSED:
+            print('Hint: qemu is not running?')
+        sys.exit(1)
+
+    if cmd == 'fsfreeze' and args[0] == 'freeze':
+        client.sync(60)
+    elif cmd != 'ping':
+        client.sync()
+
+    globals()['_cmd_' + cmd](client, args)
+
+
+if __name__ == '__main__':
+    import sys
+    import os
+    import optparse
+
+    address = os.environ['QGA_CLIENT_ADDRESS'] if 'QGA_CLIENT_ADDRESS' in os.environ else None
+
+    usage = "%prog [--address=<unix_path>|<ipv4_address>] <command> [args...]\n"
+    usage += '<command>: ' + ', '.join(commands)
+    parser = optparse.OptionParser(usage=usage)
+    parser.add_option('--address', action='store', type='string',
+                      default=address, help='Specify a ip:port pair or a unix socket path')
+    options, args = parser.parse_args()
+
+    address = options.address
+    if address is None:
+        parser.error('address is not specified')
+        sys.exit(1)
+
+    if len(args) == 0:
+        parser.error('Less argument')
+        sys.exit(1)
+
+    main(address, args[0], args[1:])
diff --git a/scripts/qmp/qmp b/scripts/qmp/qmp
new file mode 100755 (executable)
index 0000000..1db3c7f
--- /dev/null
@@ -0,0 +1,126 @@
+#!/usr/bin/python
+#
+# QMP command line tool
+#
+# Copyright IBM, Corp. 2011
+#
+# Authors:
+#  Anthony Liguori <aliguori@us.ibm.com>
+#
+# This work is licensed under the terms of the GNU GPLv2 or later.
+# See the COPYING file in the top-level directory.
+
+import sys, os
+from qmp import QEMUMonitorProtocol
+
+def print_response(rsp, prefix=[]):
+    if type(rsp) == list:
+        i = 0
+        for item in rsp:
+            if prefix == []:
+                prefix = ['item']
+            print_response(item, prefix[:-1] + ['%s[%d]' % (prefix[-1], i)])
+            i += 1
+    elif type(rsp) == dict:
+        for key in rsp.keys():
+            print_response(rsp[key], prefix + [key])
+    else:
+        if len(prefix):
+            print '%s: %s' % ('.'.join(prefix), rsp)
+        else:
+            print '%s' % (rsp)
+
+def main(args):
+    path = None
+
+    # Use QMP_PATH if it's set
+    if os.environ.has_key('QMP_PATH'):
+        path = os.environ['QMP_PATH']
+
+    while len(args):
+        arg = args[0]
+
+        if arg.startswith('--'):
+            arg = arg[2:]
+            if arg.find('=') == -1:
+                value = True
+            else:
+                arg, value = arg.split('=', 1)
+
+            if arg in ['path']:
+                if type(value) == str:
+                    path = value
+            elif arg in ['help']:
+                os.execlp('man', 'man', 'qmp')
+            else:
+                print 'Unknown argument "%s"' % arg
+
+            args = args[1:]
+        else:
+            break
+
+    if not path:
+        print "QMP path isn't set, use --path=qmp-monitor-address or set QMP_PATH"
+        return 1
+
+    if len(args):
+        command, args = args[0], args[1:]
+    else:
+        print 'No command found'
+        print 'Usage: "qmp [--path=qmp-monitor-address] qmp-cmd arguments"'
+        return 1
+
+    if command in ['help']:
+        os.execlp('man', 'man', 'qmp')
+
+    srv = QEMUMonitorProtocol(path)
+    srv.connect()
+
+    def do_command(srv, cmd, **kwds):
+        rsp = srv.cmd(cmd, kwds)
+        if rsp.has_key('error'):
+            raise Exception(rsp['error']['desc'])
+        return rsp['return']
+
+    commands = map(lambda x: x['name'], do_command(srv, 'query-commands'))
+
+    srv.close()
+
+    if command not in commands:
+        fullcmd = 'qmp-%s' % command
+        try:
+            os.environ['QMP_PATH'] = path
+            os.execvp(fullcmd, [fullcmd] + args)
+        except OSError, (errno, msg):
+            if errno == 2:
+                print 'Command "%s" not found.' % (fullcmd)
+                return 1
+            raise
+        return 0
+
+    srv = QEMUMonitorProtocol(path)
+    srv.connect()
+
+    arguments = {}
+    for arg in args:
+        if not arg.startswith('--'):
+            print 'Unknown argument "%s"' % arg
+            return 1
+
+        arg = arg[2:]
+        if arg.find('=') == -1:
+            value = True
+        else:
+            arg, value = arg.split('=', 1)
+
+        if arg in ['help']:
+            os.execlp('man', 'man', 'qmp-%s' % command)
+            return 1
+
+        arguments[arg] = value
+
+    rsp = do_command(srv, command, **arguments)
+    print_response(rsp)
+
+if __name__ == '__main__':
+    sys.exit(main(sys.argv[1:]))
diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell
new file mode 100755 (executable)
index 0000000..d6b420f
--- /dev/null
@@ -0,0 +1,286 @@
+#!/usr/bin/python
+#
+# Low-level QEMU shell on top of QMP.
+#
+# Copyright (C) 2009, 2010 Red Hat Inc.
+#
+# Authors:
+#  Luiz Capitulino <lcapitulino@redhat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2.  See
+# the COPYING file in the top-level directory.
+#
+# Usage:
+#
+# Start QEMU with:
+#
+# # qemu [...] -qmp unix:./qmp-sock,server
+#
+# Run the shell:
+#
+# $ qmp-shell ./qmp-sock
+#
+# Commands have the following format:
+#
+#    < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ]
+#
+# For example:
+#
+# (QEMU) device_add driver=e1000 id=net1
+# {u'return': {}}
+# (QEMU)
+
+import qmp
+import readline
+import sys
+import pprint
+
+class QMPCompleter(list):
+    def complete(self, text, state):
+        for cmd in self:
+            if cmd.startswith(text):
+                if not state:
+                    return cmd
+                else:
+                    state -= 1
+
+class QMPShellError(Exception):
+    pass
+
+class QMPShellBadPort(QMPShellError):
+    pass
+
+# TODO: QMPShell's interface is a bit ugly (eg. _fill_completion() and
+#       _execute_cmd()). Let's design a better one.
+class QMPShell(qmp.QEMUMonitorProtocol):
+    def __init__(self, address, pp=None):
+        qmp.QEMUMonitorProtocol.__init__(self, self.__get_address(address))
+        self._greeting = None
+        self._completer = None
+        self._pp = pp
+
+    def __get_address(self, arg):
+        """
+        Figure out if the argument is in the port:host form, if it's not it's
+        probably a file path.
+        """
+        addr = arg.split(':')
+        if len(addr) == 2:
+            try:
+                port = int(addr[1])
+            except ValueError:
+                raise QMPShellBadPort
+            return ( addr[0], port )
+        # socket path
+        return arg
+
+    def _fill_completion(self):
+        for cmd in self.cmd('query-commands')['return']:
+            self._completer.append(cmd['name'])
+
+    def __completer_setup(self):
+        self._completer = QMPCompleter()
+        self._fill_completion()
+        readline.set_completer(self._completer.complete)
+        readline.parse_and_bind("tab: complete")
+        # XXX: default delimiters conflict with some command names (eg. query-),
+        # clearing everything as it doesn't seem to matter
+        readline.set_completer_delims('')
+
+    def __build_cmd(self, cmdline):
+        """
+        Build a QMP input object from a user provided command-line in the
+        following format:
+
+            < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ]
+        """
+        cmdargs = cmdline.split()
+        qmpcmd = { 'execute': cmdargs[0], 'arguments': {} }
+        for arg in cmdargs[1:]:
+            opt = arg.split('=')
+            try:
+                if(len(opt) > 2):
+                    opt[1] = '='.join(opt[1:])
+                value = int(opt[1])
+            except ValueError:
+                if opt[1] == 'true':
+                    value = True
+                elif opt[1] == 'false':
+                    value = False
+                else:
+                    value = opt[1]
+            qmpcmd['arguments'][opt[0]] = value
+        return qmpcmd
+
+    def _execute_cmd(self, cmdline):
+        try:
+            qmpcmd = self.__build_cmd(cmdline)
+        except:
+            print 'command format: <command-name> ',
+            print '[arg-name1=arg1] ... [arg-nameN=argN]'
+            return True
+        resp = self.cmd_obj(qmpcmd)
+        if resp is None:
+            print 'Disconnected'
+            return False
+
+        if self._pp is not None:
+            self._pp.pprint(resp)
+        else:
+            print resp
+        return True
+
+    def connect(self):
+        self._greeting = qmp.QEMUMonitorProtocol.connect(self)
+        self.__completer_setup()
+
+    def show_banner(self, msg='Welcome to the QMP low-level shell!'):
+        print msg
+        version = self._greeting['QMP']['version']['qemu']
+        print 'Connected to QEMU %d.%d.%d\n' % (version['major'],version['minor'],version['micro'])
+
+    def read_exec_command(self, prompt):
+        """
+        Read and execute a command.
+
+        @return True if execution was ok, return False if disconnected.
+        """
+        try:
+            cmdline = raw_input(prompt)
+        except EOFError:
+            print
+            return False
+        if cmdline == '':
+            for ev in self.get_events():
+                print ev
+            self.clear_events()
+            return True
+        else:
+            return self._execute_cmd(cmdline)
+
+class HMPShell(QMPShell):
+    def __init__(self, address):
+        QMPShell.__init__(self, address)
+        self.__cpu_index = 0
+
+    def __cmd_completion(self):
+        for cmd in self.__cmd_passthrough('help')['return'].split('\r\n'):
+            if cmd and cmd[0] != '[' and cmd[0] != '\t':
+                name = cmd.split()[0] # drop help text
+                if name == 'info':
+                    continue
+                if name.find('|') != -1:
+                    # Command in the form 'foobar|f' or 'f|foobar', take the
+                    # full name
+                    opt = name.split('|')
+                    if len(opt[0]) == 1:
+                        name = opt[1]
+                    else:
+                        name = opt[0]
+                self._completer.append(name)
+                self._completer.append('help ' + name) # help completion
+
+    def __info_completion(self):
+        for cmd in self.__cmd_passthrough('info')['return'].split('\r\n'):
+            if cmd:
+                self._completer.append('info ' + cmd.split()[1])
+
+    def __other_completion(self):
+        # special cases
+        self._completer.append('help info')
+
+    def _fill_completion(self):
+        self.__cmd_completion()
+        self.__info_completion()
+        self.__other_completion()
+
+    def __cmd_passthrough(self, cmdline, cpu_index = 0):
+        return self.cmd_obj({ 'execute': 'human-monitor-command', 'arguments':
+                              { 'command-line': cmdline,
+                                'cpu-index': cpu_index } })
+
+    def _execute_cmd(self, cmdline):
+        if cmdline.split()[0] == "cpu":
+            # trap the cpu command, it requires special setting
+            try:
+                idx = int(cmdline.split()[1])
+                if not 'return' in self.__cmd_passthrough('info version', idx):
+                    print 'bad CPU index'
+                    return True
+                self.__cpu_index = idx
+            except ValueError:
+                print 'cpu command takes an integer argument'
+                return True
+        resp = self.__cmd_passthrough(cmdline, self.__cpu_index)
+        if resp is None:
+            print 'Disconnected'
+            return False
+        assert 'return' in resp or 'error' in resp
+        if 'return' in resp:
+            # Success
+            if len(resp['return']) > 0:
+                print resp['return'],
+        else:
+            # Error
+            print '%s: %s' % (resp['error']['class'], resp['error']['desc'])
+        return True
+
+    def show_banner(self):
+        QMPShell.show_banner(self, msg='Welcome to the HMP shell!')
+
+def die(msg):
+    sys.stderr.write('ERROR: %s\n' % msg)
+    sys.exit(1)
+
+def fail_cmdline(option=None):
+    if option:
+        sys.stderr.write('ERROR: bad command-line option \'%s\'\n' % option)
+    sys.stderr.write('qemu-shell [ -p ] [ -H ] < UNIX socket path> | < TCP address:port >\n')
+    sys.exit(1)
+
+def main():
+    addr = ''
+    qemu = None
+    hmp = False
+    pp = None
+
+    try:
+        for arg in sys.argv[1:]:
+            if arg == "-H":
+                if qemu is not None:
+                    fail_cmdline(arg)
+                hmp = True
+            elif arg == "-p":
+                if pp is not None:
+                    fail_cmdline(arg)
+                pp = pprint.PrettyPrinter(indent=4)
+            else:
+                if qemu is not None:
+                    fail_cmdline(arg)
+                if hmp:
+                    qemu = HMPShell(arg)
+                else:
+                    qemu = QMPShell(arg, pp)
+                addr = arg
+
+        if qemu is None:
+            fail_cmdline()
+    except QMPShellBadPort:
+        die('bad port number in command-line')
+
+    try:
+        qemu.connect()
+    except qmp.QMPConnectError:
+        die('Didn\'t get QMP greeting message')
+    except qmp.QMPCapabilitiesError:
+        die('Could not negotiate capabilities')
+    except qemu.error:
+        die('Could not connect to %s' % addr)
+
+    qemu.show_banner()
+    while qemu.read_exec_command('(QEMU) '):
+        pass
+    qemu.close()
+
+if __name__ == '__main__':
+    main()
diff --git a/scripts/qmp/qmp.py b/scripts/qmp/qmp.py
new file mode 100644 (file)
index 0000000..5c97175
--- /dev/null
@@ -0,0 +1,196 @@
+# QEMU Monitor Protocol Python class
+#
+# Copyright (C) 2009, 2010 Red Hat Inc.
+#
+# Authors:
+#  Luiz Capitulino <lcapitulino@redhat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2.  See
+# the COPYING file in the top-level directory.
+
+import json
+import errno
+import socket
+
+class QMPError(Exception):
+    pass
+
+class QMPConnectError(QMPError):
+    pass
+
+class QMPCapabilitiesError(QMPError):
+    pass
+
+class QEMUMonitorProtocol:
+    def __init__(self, address, server=False):
+        """
+        Create a QEMUMonitorProtocol class.
+
+        @param address: QEMU address, can be either a unix socket path (string)
+                        or a tuple in the form ( address, port ) for a TCP
+                        connection
+        @param server: server mode listens on the socket (bool)
+        @raise socket.error on socket connection errors
+        @note No connection is established, this is done by the connect() or
+              accept() methods
+        """
+        self.__events = []
+        self.__address = address
+        self.__sock = self.__get_sock()
+        if server:
+            self.__sock.bind(self.__address)
+            self.__sock.listen(1)
+
+    def __get_sock(self):
+        if isinstance(self.__address, tuple):
+            family = socket.AF_INET
+        else:
+            family = socket.AF_UNIX
+        return socket.socket(family, socket.SOCK_STREAM)
+
+    def __negotiate_capabilities(self):
+        greeting = self.__json_read()
+        if greeting is None or not greeting.has_key('QMP'):
+            raise QMPConnectError
+        # Greeting seems ok, negotiate capabilities
+        resp = self.cmd('qmp_capabilities')
+        if "return" in resp:
+            return greeting
+        raise QMPCapabilitiesError
+
+    def __json_read(self, only_event=False):
+        while True:
+            data = self.__sockfile.readline()
+            if not data:
+                return
+            resp = json.loads(data)
+            if 'event' in resp:
+                self.__events.append(resp)
+                if not only_event:
+                    continue
+            return resp
+
+    error = socket.error
+
+    def connect(self, negotiate=True):
+        """
+        Connect to the QMP Monitor and perform capabilities negotiation.
+
+        @return QMP greeting dict
+        @raise socket.error on socket connection errors
+        @raise QMPConnectError if the greeting is not received
+        @raise QMPCapabilitiesError if fails to negotiate capabilities
+        """
+        self.__sock.connect(self.__address)
+        self.__sockfile = self.__sock.makefile()
+        if negotiate:
+            return self.__negotiate_capabilities()
+
+    def accept(self):
+        """
+        Await connection from QMP Monitor and perform capabilities negotiation.
+
+        @return QMP greeting dict
+        @raise socket.error on socket connection errors
+        @raise QMPConnectError if the greeting is not received
+        @raise QMPCapabilitiesError if fails to negotiate capabilities
+        """
+        self.__sock, _ = self.__sock.accept()
+        self.__sockfile = self.__sock.makefile()
+        return self.__negotiate_capabilities()
+
+    def cmd_obj(self, qmp_cmd):
+        """
+        Send a QMP command to the QMP Monitor.
+
+        @param qmp_cmd: QMP command to be sent as a Python dict
+        @return QMP response as a Python dict or None if the connection has
+                been closed
+        """
+        try:
+            self.__sock.sendall(json.dumps(qmp_cmd))
+        except socket.error, err:
+            if err[0] == errno.EPIPE:
+                return
+            raise socket.error(err)
+        return self.__json_read()
+
+    def cmd(self, name, args=None, id=None):
+        """
+        Build a QMP command and send it to the QMP Monitor.
+
+        @param name: command name (string)
+        @param args: command arguments (dict)
+        @param id: command id (dict, list, string or int)
+        """
+        qmp_cmd = { 'execute': name }
+        if args:
+            qmp_cmd['arguments'] = args
+        if id:
+            qmp_cmd['id'] = id
+        return self.cmd_obj(qmp_cmd)
+
+    def command(self, cmd, **kwds):
+        ret = self.cmd(cmd, kwds)
+        if ret.has_key('error'):
+            raise Exception(ret['error']['desc'])
+        return ret['return']
+
+    def pull_event(self, wait=False):
+        """
+        Get and delete the first available QMP event.
+
+        @param wait: block until an event is available (bool)
+        """
+        self.__sock.setblocking(0)
+        try:
+            self.__json_read()
+        except socket.error, err:
+            if err[0] == errno.EAGAIN:
+                # No data available
+                pass
+        self.__sock.setblocking(1)
+        if not self.__events and wait:
+            self.__json_read(only_event=True)
+        event = self.__events[0]
+        del self.__events[0]
+        return event
+
+    def get_events(self, wait=False):
+        """
+        Get a list of available QMP events.
+
+        @param wait: block until an event is available (bool)
+        """
+        self.__sock.setblocking(0)
+        try:
+            self.__json_read()
+        except socket.error, err:
+            if err[0] == errno.EAGAIN:
+                # No data available
+                pass
+        self.__sock.setblocking(1)
+        if not self.__events and wait:
+            self.__json_read(only_event=True)
+        return self.__events
+
+    def clear_events(self):
+        """
+        Clear current list of pending events.
+        """
+        self.__events = []
+
+    def close(self):
+        self.__sock.close()
+        self.__sockfile.close()
+
+    timeout = socket.timeout
+
+    def settimeout(self, timeout):
+        self.__sock.settimeout(timeout)
+
+    def get_sock_fd(self):
+        return self.__sock.fileno()
+
+    def is_scm_available(self):
+        return self.__sock.family == socket.AF_UNIX
diff --git a/scripts/qmp/qom-fuse b/scripts/qmp/qom-fuse
new file mode 100755 (executable)
index 0000000..5c6754a
--- /dev/null
@@ -0,0 +1,138 @@
+#!/usr/bin/python
+##
+# QEMU Object Model test tools
+#
+# Copyright IBM, Corp. 2012
+#
+# Authors:
+#  Anthony Liguori   <aliguori@us.ibm.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or later.  See
+# the COPYING file in the top-level directory.
+##
+
+import fuse, stat
+from fuse import Fuse
+import os, posix
+from errno import *
+from qmp import QEMUMonitorProtocol
+
+fuse.fuse_python_api = (0, 2)
+
+class QOMFS(Fuse):
+    def __init__(self, qmp, *args, **kwds):
+        Fuse.__init__(self, *args, **kwds)
+        self.qmp = qmp
+        self.qmp.connect()
+        self.ino_map = {}
+        self.ino_count = 1
+
+    def get_ino(self, path):
+        if self.ino_map.has_key(path):
+            return self.ino_map[path]
+        self.ino_map[path] = self.ino_count
+        self.ino_count += 1
+        return self.ino_map[path]
+
+    def is_object(self, path):
+        try:
+            items = self.qmp.command('qom-list', path=path)
+            return True
+        except:
+            return False
+
+    def is_property(self, path):
+        try:
+            path, prop = path.rsplit('/', 1)
+            for item in self.qmp.command('qom-list', path=path):
+                if item['name'] == prop:
+                    return True
+            return False
+        except:
+            return False
+
+    def is_link(self, path):
+        try:
+            path, prop = path.rsplit('/', 1)
+            for item in self.qmp.command('qom-list', path=path):
+                if item['name'] == prop:
+                    if item['type'].startswith('link<'):
+                        return True
+                    return False
+            return False
+        except:
+            return False
+
+    def read(self, path, length, offset):
+        if not self.is_property(path):
+            return -ENOENT
+
+        path, prop = path.rsplit('/', 1)
+        try:
+            data = str(self.qmp.command('qom-get', path=path, property=prop))
+            data += '\n' # make values shell friendly
+        except:
+            return -EPERM
+
+        if offset > len(data):
+            return ''
+
+        return str(data[offset:][:length])
+
+    def readlink(self, path):
+        if not self.is_link(path):
+            return False
+        path, prop = path.rsplit('/', 1)
+        prefix = '/'.join(['..'] * (len(path.split('/')) - 1))
+        return prefix + str(self.qmp.command('qom-get', path=path,
+                                             property=prop))
+
+    def getattr(self, path):
+        if self.is_link(path):
+            value = posix.stat_result((0755 | stat.S_IFLNK,
+                                       self.get_ino(path),
+                                       0,
+                                       2,
+                                       1000,
+                                       1000,
+                                       4096,
+                                       0,
+                                       0,
+                                       0))
+        elif self.is_object(path):
+            value = posix.stat_result((0755 | stat.S_IFDIR,
+                                       self.get_ino(path),
+                                       0,
+                                       2,
+                                       1000,
+                                       1000,
+                                       4096,
+                                       0,
+                                       0,
+                                       0))
+        elif self.is_property(path):
+            value = posix.stat_result((0644 | stat.S_IFREG,
+                                       self.get_ino(path),
+                                       0,
+                                       1,
+                                       1000,
+                                       1000,
+                                       4096,
+                                       0,
+                                       0,
+                                       0))
+        else:
+            value = -ENOENT
+        return value
+
+    def readdir(self, path, offset):
+        yield fuse.Direntry('.')
+        yield fuse.Direntry('..')
+        for item in self.qmp.command('qom-list', path=path):
+            yield fuse.Direntry(str(item['name']))
+
+if __name__ == '__main__':
+    import sys, os
+
+    fs = QOMFS(QEMUMonitorProtocol(os.environ['QMP_SOCKET']))
+    fs.main(sys.argv)
diff --git a/scripts/qmp/qom-get b/scripts/qmp/qom-get
new file mode 100755 (executable)
index 0000000..0172c69
--- /dev/null
@@ -0,0 +1,67 @@
+#!/usr/bin/python
+##
+# QEMU Object Model test tools
+#
+# Copyright IBM, Corp. 2011
+#
+# Authors:
+#  Anthony Liguori   <aliguori@us.ibm.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or later.  See
+# the COPYING file in the top-level directory.
+##
+
+import sys
+import os
+from qmp import QEMUMonitorProtocol
+
+cmd, args = sys.argv[0], sys.argv[1:]
+socket_path = None
+path = None
+prop = None
+
+def usage():
+    return '''environment variables:
+    QMP_SOCKET=<path | addr:port>
+usage:
+    %s [-h] [-s <QMP socket path | addr:port>] <path>.<property>
+''' % cmd
+
+def usage_error(error_msg = "unspecified error"):
+    sys.stderr.write('%s\nERROR: %s\n' % (usage(), error_msg))
+    exit(1)
+
+if len(args) > 0:
+    if args[0] == "-h":
+        print usage()
+        exit(0);
+    elif args[0] == "-s":
+        try:
+            socket_path = args[1]
+        except:
+            usage_error("missing argument: QMP socket path or address");
+        args = args[2:]
+
+if not socket_path:
+    if os.environ.has_key('QMP_SOCKET'):
+        socket_path = os.environ['QMP_SOCKET']
+    else:
+        usage_error("no QMP socket path or address given");
+
+if len(args) > 0:
+    try:
+        path, prop = args[0].rsplit('.', 1)
+    except:
+        usage_error("invalid format for path/property/value")
+else:
+    usage_error("not enough arguments")
+
+srv = QEMUMonitorProtocol(socket_path)
+srv.connect()
+
+rsp = srv.command('qom-get', path=path, property=prop)
+if type(rsp) == dict:
+    for i in rsp.keys():
+        print '%s: %s' % (i, rsp[i])
+else:
+    print rsp
diff --git a/scripts/qmp/qom-list b/scripts/qmp/qom-list
new file mode 100755 (executable)
index 0000000..1e7cc6c
--- /dev/null
@@ -0,0 +1,64 @@
+#!/usr/bin/python
+##
+# QEMU Object Model test tools
+#
+# Copyright IBM, Corp. 2011
+#
+# Authors:
+#  Anthony Liguori   <aliguori@us.ibm.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or later.  See
+# the COPYING file in the top-level directory.
+##
+
+import sys
+import os
+from qmp import QEMUMonitorProtocol
+
+cmd, args = sys.argv[0], sys.argv[1:]
+socket_path = None
+path = None
+prop = None
+
+def usage():
+    return '''environment variables:
+    QMP_SOCKET=<path | addr:port>
+usage:
+    %s [-h] [-s <QMP socket path | addr:port>] [<path>]
+''' % cmd
+
+def usage_error(error_msg = "unspecified error"):
+    sys.stderr.write('%s\nERROR: %s\n' % (usage(), error_msg))
+    exit(1)
+
+if len(args) > 0:
+    if args[0] == "-h":
+        print usage()
+        exit(0);
+    elif args[0] == "-s":
+        try:
+            socket_path = args[1]
+        except:
+            usage_error("missing argument: QMP socket path or address");
+        args = args[2:]
+
+if not socket_path:
+    if os.environ.has_key('QMP_SOCKET'):
+        socket_path = os.environ['QMP_SOCKET']
+    else:
+        usage_error("no QMP socket path or address given");
+
+srv = QEMUMonitorProtocol(socket_path)
+srv.connect()
+
+if len(args) == 0:
+    print '/'
+    sys.exit(0)
+
+for item in srv.command('qom-list', path=args[0]):
+    if item['type'].startswith('child<'):
+        print '%s/' % item['name']
+    elif item['type'].startswith('link<'):
+        print '@%s/' % item['name']
+    else:
+        print '%s' % item['name']
diff --git a/scripts/qmp/qom-set b/scripts/qmp/qom-set
new file mode 100755 (executable)
index 0000000..54ecfec
--- /dev/null
@@ -0,0 +1,64 @@
+#!/usr/bin/python
+##
+# QEMU Object Model test tools
+#
+# Copyright IBM, Corp. 2011
+#
+# Authors:
+#  Anthony Liguori   <aliguori@us.ibm.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or later.  See
+# the COPYING file in the top-level directory.
+##
+
+import sys
+import os
+from qmp import QEMUMonitorProtocol
+
+cmd, args = sys.argv[0], sys.argv[1:]
+socket_path = None
+path = None
+prop = None
+value = None
+
+def usage():
+    return '''environment variables:
+    QMP_SOCKET=<path | addr:port>
+usage:
+    %s [-h] [-s <QMP socket path | addr:port>] <path>.<property> <value>
+''' % cmd
+
+def usage_error(error_msg = "unspecified error"):
+    sys.stderr.write('%s\nERROR: %s\n' % (usage(), error_msg))
+    exit(1)
+
+if len(args) > 0:
+    if args[0] == "-h":
+        print usage()
+        exit(0);
+    elif args[0] == "-s":
+        try:
+            socket_path = args[1]
+        except:
+            usage_error("missing argument: QMP socket path or address");
+        args = args[2:]
+
+if not socket_path:
+    if os.environ.has_key('QMP_SOCKET'):
+        socket_path = os.environ['QMP_SOCKET']
+    else:
+        usage_error("no QMP socket path or address given");
+
+if len(args) > 1:
+    try:
+        path, prop = args[0].rsplit('.', 1)
+    except:
+        usage_error("invalid format for path/property/value")
+    value = args[1]
+else:
+    usage_error("not enough arguments")
+
+srv = QEMUMonitorProtocol(socket_path)
+srv.connect()
+
+print srv.command('qom-set', path=path, property=prop, value=sys.argv[2])
index ceabff81b2cc85f4453a262ddb0a6285bd2aac33..5bdcbd50f7ec4acc41b4017d8f538d2f3127a4b2 100644 (file)
@@ -16,8 +16,7 @@ Slirp *slirp_init(int restricted, struct in_addr vnetwork,
                   void *opaque);
 void slirp_cleanup(Slirp *slirp);
 
-void slirp_update_timeout(uint32_t *timeout);
-void slirp_pollfds_fill(GArray *pollfds);
+void slirp_pollfds_fill(GArray *pollfds, uint32_t *timeout);
 
 void slirp_pollfds_poll(GArray *pollfds, int select_error);
 
index 5c3dabba9342ebd8eea1bf9d144d864c4d340416..bad8dad02e97e5350bf966dfa627ccd862d3edd2 100644 (file)
@@ -40,8 +40,6 @@ static const uint8_t special_ethaddr[ETH_ALEN] = {
 static const uint8_t zero_ethaddr[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
 
 u_int curtime;
-static u_int time_fasttimo, last_slowtimo;
-static int do_slowtimo;
 
 static QTAILQ_HEAD(slirp_instances, Slirp) slirp_instances =
     QTAILQ_HEAD_INITIALIZER(slirp_instances);
@@ -49,6 +47,11 @@ static QTAILQ_HEAD(slirp_instances, Slirp) slirp_instances =
 static struct in_addr dns_addr;
 static u_int dns_addr_time;
 
+#define TIMEOUT_FAST 2  /* milliseconds */
+#define TIMEOUT_SLOW 499  /* milliseconds */
+/* for the aging of certain requests like DNS */
+#define TIMEOUT_DEFAULT 1000  /* milliseconds */
+
 #ifdef _WIN32
 
 int get_dns_addr(struct in_addr *pdns_addr)
@@ -59,7 +62,7 @@ int get_dns_addr(struct in_addr *pdns_addr)
     IP_ADDR_STRING *pIPAddr;
     struct in_addr tmp_addr;
 
-    if (dns_addr.s_addr != 0 && (curtime - dns_addr_time) < 1000) {
+    if (dns_addr.s_addr != 0 && (curtime - dns_addr_time) < TIMEOUT_DEFAULT) {
         *pdns_addr = dns_addr;
         return 0;
     }
@@ -115,7 +118,7 @@ int get_dns_addr(struct in_addr *pdns_addr)
 
     if (dns_addr.s_addr != 0) {
         struct stat old_stat;
-        if ((curtime - dns_addr_time) < 1000) {
+        if ((curtime - dns_addr_time) < TIMEOUT_DEFAULT) {
             *pdns_addr = dns_addr;
             return 0;
         }
@@ -259,14 +262,33 @@ void slirp_cleanup(Slirp *slirp)
 #define CONN_CANFSEND(so) (((so)->so_state & (SS_FCANTSENDMORE|SS_ISFCONNECTED)) == SS_ISFCONNECTED)
 #define CONN_CANFRCV(so) (((so)->so_state & (SS_FCANTRCVMORE|SS_ISFCONNECTED)) == SS_ISFCONNECTED)
 
-void slirp_update_timeout(uint32_t *timeout)
+static void slirp_update_timeout(uint32_t *timeout)
 {
-    if (!QTAILQ_EMPTY(&slirp_instances)) {
-        *timeout = MIN(1000, *timeout);
+    Slirp *slirp;
+    uint32_t t;
+
+    if (*timeout <= TIMEOUT_FAST) {
+        return;
+    }
+
+    t = MIN(1000, *timeout);
+
+    /* If we have tcp timeout with slirp, then we will fill @timeout with
+     * more precise value.
+     */
+    QTAILQ_FOREACH(slirp, &slirp_instances, entry) {
+        if (slirp->time_fasttimo) {
+            *timeout = TIMEOUT_FAST;
+            return;
+        }
+        if (slirp->do_slowtimo) {
+            t = MIN(TIMEOUT_SLOW, t);
+        }
     }
+    *timeout = t;
 }
 
-void slirp_pollfds_fill(GArray *pollfds)
+void slirp_pollfds_fill(GArray *pollfds, uint32_t *timeout)
 {
     Slirp *slirp;
     struct socket *so, *so_next;
@@ -278,14 +300,13 @@ void slirp_pollfds_fill(GArray *pollfds)
     /*
      * First, TCP sockets
      */
-    do_slowtimo = 0;
 
     QTAILQ_FOREACH(slirp, &slirp_instances, entry) {
         /*
          * *_slowtimo needs calling if there are IP fragments
          * in the fragment queue, or there are TCP connections active
          */
-        do_slowtimo |= ((slirp->tcb.so_next != &slirp->tcb) ||
+        slirp->do_slowtimo = ((slirp->tcb.so_next != &slirp->tcb) ||
                 (&slirp->ipq.ip_link != slirp->ipq.ip_link.next));
 
         for (so = slirp->tcb.so_next; so != &slirp->tcb;
@@ -299,8 +320,9 @@ void slirp_pollfds_fill(GArray *pollfds)
             /*
              * See if we need a tcp_fasttimo
              */
-            if (time_fasttimo == 0 && so->so_tcpcb->t_flags & TF_DELACK) {
-                time_fasttimo = curtime; /* Flag when we want a fasttimo */
+            if (slirp->time_fasttimo == 0 &&
+                so->so_tcpcb->t_flags & TF_DELACK) {
+                slirp->time_fasttimo = curtime; /* Flag when want a fasttimo */
             }
 
             /*
@@ -381,7 +403,7 @@ void slirp_pollfds_fill(GArray *pollfds)
                     udp_detach(so);
                     continue;
                 } else {
-                    do_slowtimo = 1; /* Let socket expire */
+                    slirp->do_slowtimo = true; /* Let socket expire */
                 }
             }
 
@@ -422,7 +444,7 @@ void slirp_pollfds_fill(GArray *pollfds)
                     icmp_detach(so);
                     continue;
                 } else {
-                    do_slowtimo = 1; /* Let socket expire */
+                    slirp->do_slowtimo = true; /* Let socket expire */
                 }
             }
 
@@ -436,6 +458,7 @@ void slirp_pollfds_fill(GArray *pollfds)
             }
         }
     }
+    slirp_update_timeout(timeout);
 }
 
 void slirp_pollfds_poll(GArray *pollfds, int select_error)
@@ -454,14 +477,16 @@ void slirp_pollfds_poll(GArray *pollfds, int select_error)
         /*
          * See if anything has timed out
          */
-        if (time_fasttimo && ((curtime - time_fasttimo) >= 2)) {
+        if (slirp->time_fasttimo &&
+            ((curtime - slirp->time_fasttimo) >= TIMEOUT_FAST)) {
             tcp_fasttimo(slirp);
-            time_fasttimo = 0;
+            slirp->time_fasttimo = 0;
         }
-        if (do_slowtimo && ((curtime - last_slowtimo) >= 499)) {
+        if (slirp->do_slowtimo &&
+            ((curtime - slirp->last_slowtimo) >= TIMEOUT_SLOW)) {
             ip_slowtimo(slirp);
             tcp_slowtimo(slirp);
-            last_slowtimo = curtime;
+            slirp->last_slowtimo = curtime;
         }
 
         /*
index fe0e65d0eee0bd1442caf2a8df3a67a8d6fd28b3..e4a1bd4abb19e9e24759edd633d1db119a8313b3 100644 (file)
@@ -203,6 +203,9 @@ bool arp_table_search(Slirp *slirp, uint32_t ip_addr,
 
 struct Slirp {
     QTAILQ_ENTRY(Slirp) entry;
+    u_int time_fasttimo;
+    u_int last_slowtimo;
+    bool do_slowtimo;
 
     /* virtual network configuration */
     struct in_addr vnetwork_addr;
index f306cbada3753632147aa94b7aad0df6626c60ff..df92fe5b15825ae521237c223ba8759569779c11 100644 (file)
@@ -22,6 +22,7 @@ stub-obj-y += reset.o
 stub-obj-y += set-fd-handler.o
 stub-obj-y += slirp.o
 stub-obj-y += sysbus.o
+stub-obj-y += uuid.o
 stub-obj-y += vm-stop.o
 stub-obj-y += vmstate.o
 stub-obj-$(CONFIG_WIN32) += fd-register.o
index f1fc833f7aa9e981e2b9913e87bff2749af5f8a9..bd0ac7f27de4374c447f983cea8ebd2209e20fff 100644 (file)
@@ -1,11 +1,7 @@
 #include "qemu-common.h"
 #include "slirp/slirp.h"
 
-void slirp_update_timeout(uint32_t *timeout)
-{
-}
-
-void slirp_pollfds_fill(GArray *pollfds)
+void slirp_pollfds_fill(GArray *pollfds, uint32_t *timeout)
 {
 }
 
diff --git a/stubs/uuid.c b/stubs/uuid.c
new file mode 100644 (file)
index 0000000..ffc0ed4
--- /dev/null
@@ -0,0 +1,12 @@
+#include "qemu-common.h"
+#include "sysemu/sysemu.h"
+#include "qmp-commands.h"
+
+UuidInfo *qmp_query_uuid(Error **errp)
+{
+    UuidInfo *info = g_malloc0(sizeof(*info));
+
+    info->UUID = g_strdup(UUID_NONE);
+    return info;
+}
+
index 0ad7c8e3b6d2d5abcd59258b9418614b51831909..678503a7cc375f3af0189a1b67b29077695a670b 100644 (file)
@@ -892,7 +892,10 @@ hwaddr x86_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
     uint32_t page_offset;
     int page_size;
 
-    if (env->cr[4] & CR4_PAE_MASK) {
+    if (!(env->cr[0] & CR0_PG_MASK)) {
+        pte = addr & env->a20_mask;
+        page_size = 4096;
+    } else if (env->cr[4] & CR4_PAE_MASK) {
         target_ulong pdpe_addr;
         uint64_t pde, pdpe;
 
@@ -950,26 +953,21 @@ hwaddr x86_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
     } else {
         uint32_t pde;
 
-        if (!(env->cr[0] & CR0_PG_MASK)) {
-            pte = addr;
-            page_size = 4096;
+        /* page directory entry */
+        pde_addr = ((env->cr[3] & ~0xfff) + ((addr >> 20) & 0xffc)) & env->a20_mask;
+        pde = ldl_phys(pde_addr);
+        if (!(pde & PG_PRESENT_MASK))
+            return -1;
+        if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) {
+            pte = pde & ~0x003ff000; /* align to 4MB */
+            page_size = 4096 * 1024;
         } else {
             /* page directory entry */
-            pde_addr = ((env->cr[3] & ~0xfff) + ((addr >> 20) & 0xffc)) & env->a20_mask;
-            pde = ldl_phys(pde_addr);
-            if (!(pde & PG_PRESENT_MASK))
+            pte_addr = ((pde & ~0xfff) + ((addr >> 10) & 0xffc)) & env->a20_mask;
+            pte = ldl_phys(pte_addr);
+            if (!(pte & PG_PRESENT_MASK))
                 return -1;
-            if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) {
-                pte = pde & ~0x003ff000; /* align to 4MB */
-                page_size = 4096 * 1024;
-            } else {
-                /* page directory entry */
-                pte_addr = ((pde & ~0xfff) + ((addr >> 10) & 0xffc)) & env->a20_mask;
-                pte = ldl_phys(pte_addr);
-                if (!(pte & PG_PRESENT_MASK))
-                    return -1;
-                page_size = 4096;
-            }
+            page_size = 4096;
         }
         pte = pte & env->a20_mask;
     }
index 6d879003b32aa40d22fd454ce24a765a7d826004..be74ebc278908b34477b3582d8ad227fae8e9475 100644 (file)
@@ -6434,12 +6434,18 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
                 }
                 break;
             case 0x1d: /* fucomi */
+                if (!(s->cpuid_features & CPUID_CMOV)) {
+                    goto illegal_op;
+                }
                 gen_update_cc_op(s);
                 gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg));
                 gen_helper_fucomi_ST0_FT0(cpu_env);
                 set_cc_op(s, CC_OP_EFLAGS);
                 break;
             case 0x1e: /* fcomi */
+                if (!(s->cpuid_features & CPUID_CMOV)) {
+                    goto illegal_op;
+                }
                 gen_update_cc_op(s);
                 gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg));
                 gen_helper_fcomi_ST0_FT0(cpu_env);
@@ -6495,6 +6501,9 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
                 }
                 break;
             case 0x3d: /* fucomip */
+                if (!(s->cpuid_features & CPUID_CMOV)) {
+                    goto illegal_op;
+                }
                 gen_update_cc_op(s);
                 gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg));
                 gen_helper_fucomi_ST0_FT0(cpu_env);
@@ -6502,6 +6511,9 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
                 set_cc_op(s, CC_OP_EFLAGS);
                 break;
             case 0x3e: /* fcomip */
+                if (!(s->cpuid_features & CPUID_CMOV)) {
+                    goto illegal_op;
+                }
                 gen_update_cc_op(s);
                 gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg));
                 gen_helper_fcomi_ST0_FT0(cpu_env);
@@ -6518,6 +6530,10 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
                         (JCC_BE << 1),
                         (JCC_P << 1),
                     };
+
+                    if (!(s->cpuid_features & CPUID_CMOV)) {
+                        goto illegal_op;
+                    }
                     op1 = fcmov_cc[op & 3] | (((op >> 3) & 1) ^ 1);
                     l1 = gen_new_label();
                     gen_jcc1_noeob(s, op1, l1);
@@ -6889,6 +6905,9 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
         gen_ldst_modrm(env, s, modrm, OT_BYTE, OR_TMP0, 1);
         break;
     case 0x140 ... 0x14f: /* cmov Gv, Ev */
+        if (!(s->cpuid_features & CPUID_CMOV)) {
+            goto illegal_op;
+        }
         ot = dflag + OT_WORD;
         modrm = cpu_ldub_code(env, s->pc++);
         reg = ((modrm >> 3) & 7) | rex_r;
index 185c8f5a457cf5bb9297a94007d595346dda97f9..4923e0a71752e306321408d38f8cbb5d02ff13a1 100644 (file)
@@ -93,9 +93,15 @@ const KVMCapabilityInfo kvm_arch_required_capabilities[] = {
 
 static int cap_sync_regs;
 
+static void *legacy_s390_alloc(ram_addr_t size);
+
 int kvm_arch_init(KVMState *s)
 {
     cap_sync_regs = kvm_check_extension(s, KVM_CAP_SYNC_REGS);
+    if (!kvm_check_extension(s, KVM_CAP_S390_GMAP)
+        || !kvm_check_extension(s, KVM_CAP_S390_COW)) {
+        phys_mem_set_alloc(legacy_s390_alloc);
+    }
     return 0;
 }
 
@@ -326,22 +332,7 @@ static void *legacy_s390_alloc(ram_addr_t size)
     mem = mmap((void *) 0x800000000ULL, size,
                PROT_EXEC|PROT_READ|PROT_WRITE,
                MAP_SHARED | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
-    if (mem == MAP_FAILED) {
-        fprintf(stderr, "Allocating RAM failed\n");
-        abort();
-    }
-    return mem;
-}
-
-void *kvm_arch_ram_alloc(ram_addr_t size)
-{
-    /* Can we use the standard allocation ? */
-    if (kvm_check_extension(kvm_state, KVM_CAP_S390_GMAP) &&
-        kvm_check_extension(kvm_state, KVM_CAP_S390_COW)) {
-        return NULL;
-    } else {
-        return legacy_s390_alloc(size);
-    }
+    return mem == MAP_FAILED ? NULL : mem;
 }
 
 int kvm_arch_insert_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp)
index c13fefc314b67a0815f93852dfcdc0cf936e6594..994fef183932dd55fd964a4f87d42a4941a9b12a 100644 (file)
@@ -174,6 +174,7 @@ tests/boot-order-test$(EXESUF): tests/boot-order-test.o $(libqos-obj-y)
 tests/tmp105-test$(EXESUF): tests/tmp105-test.o $(libqos-omap-obj-y)
 tests/i440fx-test$(EXESUF): tests/i440fx-test.o $(libqos-pc-obj-y)
 tests/fw_cfg-test$(EXESUF): tests/fw_cfg-test.o $(libqos-pc-obj-y)
+tests/qemu-iotests/socket_scm_helper$(EXESUF): tests/qemu-iotests/socket_scm_helper.o
 
 # QTest rules
 
@@ -252,7 +253,7 @@ check-report.html: check-report.xml
 # Other tests
 
 .PHONY: check-tests/qemu-iotests-quick.sh
-check-tests/qemu-iotests-quick.sh: tests/qemu-iotests-quick.sh qemu-img$(EXESUF) qemu-io$(EXESUF)
+check-tests/qemu-iotests-quick.sh: tests/qemu-iotests-quick.sh qemu-img$(EXESUF) qemu-io$(EXESUF) tests/qemu-iotests/socket_scm_helper$(EXESUF)
        $<
 
 .PHONY: check-tests/test-qapi.py
index c454f2c8ec816552b46c0e0d852786f1c7f86451..6fa760330ddbd6329619ee140fa979ff81f1dc56 100755 (executable)
@@ -30,7 +30,7 @@ status=1      # failure is the default!
 
 _cleanup()
 {
-#      _cleanup_test_img
+       _cleanup_test_img
        true
 }
 trap "_cleanup; exit \$status" 0 1 2 3 15
index 36125eab1e7e85eb4fddb6aa86a45226a9ef256a..90de1a73d9fbd2b6e1eb3791ec74224f9e29f73e 100755 (executable)
@@ -95,7 +95,8 @@ function overlay_io()
 }
 
 overlay_io | $QEMU_IO $TEST_IMG | _filter_qemu_io |\
-       sed -e 's/bytes at offset [0-9]*/bytes at offset XXX/g'
+    sed -e 's/bytes at offset [0-9]*/bytes at offset XXX/g' \
+    -e 's/qemu-io> //g' | paste - - | sort | tr '\t' '\n'
 
 echo
 echo "== Verify image content =="
index 9cd0cd8771afe1e20597b696a70e93d17db11bb6..96c2f849bbc198e6f6a5cc115f823af1a16d6fec 100644 (file)
@@ -517,7 +517,7 @@ qemu-io> wrote 65536/65536 bytes at offset 16711680
 qemu-io> Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file='TEST_DIR/t.IMGFMT.base' 
 
 == Some concurrent requests touching the same cluster ==
-qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> wrote 65536/65536 bytes at offset XXX
+wrote 65536/65536 bytes at offset XXX
 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
 wrote 65536/65536 bytes at offset XXX
 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
@@ -577,8 +577,6 @@ wrote 65536/65536 bytes at offset XXX
 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
 wrote 65536/65536 bytes at offset XXX
 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-wrote 81920/81920 bytes at offset XXX
-80 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
 wrote 65536/65536 bytes at offset XXX
 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
 wrote 65536/65536 bytes at offset XXX
@@ -645,8 +643,6 @@ wrote 65536/65536 bytes at offset XXX
 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
 wrote 65536/65536 bytes at offset XXX
 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-wrote 81920/81920 bytes at offset XXX
-80 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
 wrote 65536/65536 bytes at offset XXX
 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
 wrote 65536/65536 bytes at offset XXX
@@ -705,6 +701,10 @@ wrote 65536/65536 bytes at offset XXX
 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
 wrote 65536/65536 bytes at offset XXX
 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 81920/81920 bytes at offset XXX
+80 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 81920/81920 bytes at offset XXX
+80 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
 
 == Verify image content ==
 qemu-io> read 4096/4096 bytes at offset 2064384
index 2b6f1af27acd7ccc7e0dabf0e67a56b56668980b..6be8fc4912b73e91cb313de99007041685af8957 100755 (executable)
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 #
-# Tests for fdsets.
+# Tests for fdsets and getfd.
 #
 # Copyright (C) 2012 IBM Corp.
 #
@@ -125,5 +125,54 @@ class TestFdSets(iotests.QMPTestCase):
                 'No file descriptor supplied via SCM_RIGHTS')
         self.vm.shutdown()
 
+# Add fd at runtime, there are two ways: monitor related or fdset related
+class TestSCMFd(iotests.QMPTestCase):
+    def setUp(self):
+        self.vm = iotests.VM()
+        qemu_img('create', '-f', iotests.imgfmt, image0, '128K')
+        # Add an unused monitor, to verify it works fine when two monitor
+        # instances present
+        self.vm.add_monitor_telnet("0",4445)
+        self.vm.launch()
+
+    def tearDown(self):
+        self.vm.shutdown()
+        os.remove(image0)
+
+    def _send_fd_by_SCM(self):
+        ret = self.vm.send_fd_scm(image0)
+        self.assertEqual(ret, 0, 'Failed to send fd with UNIX SCM')
+
+    def test_add_fd(self):
+        self._send_fd_by_SCM()
+        result = self.vm.qmp('add-fd', fdset_id=2, opaque='image0:r')
+        self.assert_qmp(result, 'return/fdset-id', 2)
+
+    def test_getfd(self):
+        self._send_fd_by_SCM()
+        result = self.vm.qmp('getfd', fdname='image0:r')
+        self.assert_qmp(result, 'return', {})
+
+    def test_getfd_invalid_fdname(self):
+        self._send_fd_by_SCM()
+        result = self.vm.qmp('getfd', fdname='0image0:r')
+        self.assert_qmp(result, 'error/class', 'GenericError')
+        self.assert_qmp(result, 'error/desc',
+            "Parameter 'fdname' expects a name not starting with a digit")
+
+    def test_closefd(self):
+        self._send_fd_by_SCM()
+        result = self.vm.qmp('getfd', fdname='image0:r')
+        self.assert_qmp(result, 'return', {})
+        result = self.vm.qmp('closefd', fdname='image0:r')
+        self.assert_qmp(result, 'return', {})
+
+    def test_closefd_fd_not_found(self):
+        fdname = 'image0:r'
+        result = self.vm.qmp('closefd', fdname=fdname)
+        self.assert_qmp(result, 'error/class', 'GenericError')
+        self.assert_qmp(result, 'error/desc',
+            "File descriptor named '%s' not found" % fdname)
+
 if __name__ == '__main__':
     iotests.main(supported_fmts=['raw'])
index 3f8a935a082d8dde81eafb46b644fdff9d1a0cce..e56cae021be2b321abc655300cb9215a7b32f530 100644 (file)
@@ -1,5 +1,5 @@
-......
+...........
 ----------------------------------------------------------------------
-Ran 6 tests
+Ran 11 tests
 
 OK
index d2f0efe16df462bdece36d60d3598039b60ecff6..ceb23289fd9dc69b36bfe9ec2aa49e276d0864a0 100644 (file)
@@ -96,7 +96,7 @@ qemu-img: Image size must be less than 8 EiB!
 
 qemu-img create -f qcow2 -o size=-1024 TEST_DIR/t.qcow2
 qemu-img: qcow2 doesn't support shrinking images yet
-qemu-img: Formatting or formatting option not supported for file format 'qcow2'
+qemu-img: TEST_DIR/t.qcow2: Could not resize image: Operation not supported
 Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=-1024 encryption=off cluster_size=65536 lazy_refcounts=off 
 
 qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- -1k
@@ -104,7 +104,7 @@ qemu-img: Image size must be less than 8 EiB!
 
 qemu-img create -f qcow2 -o size=-1k TEST_DIR/t.qcow2
 qemu-img: qcow2 doesn't support shrinking images yet
-qemu-img: Formatting or formatting option not supported for file format 'qcow2'
+qemu-img: TEST_DIR/t.qcow2: Could not resize image: Operation not supported
 Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=-1024 encryption=off cluster_size=65536 lazy_refcounts=off 
 
 qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- 1kilobyte
@@ -120,7 +120,7 @@ qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes.
 
 qemu-img create -f qcow2 -o size=foobar TEST_DIR/t.qcow2
 qemu-img: Parameter 'size' expects a size
-qemu-img: Invalid options for file format 'qcow2'.
+qemu-img: TEST_DIR/t.qcow2: Invalid options for file format 'qcow2'.
 
 == Check correct interpretation of suffixes for cluster size ==
 
@@ -163,13 +163,11 @@ qemu-img create -f qcow2 -o compat=1.1 TEST_DIR/t.qcow2 64M
 Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='1.1' encryption=off cluster_size=65536 lazy_refcounts=off 
 
 qemu-img create -f qcow2 -o compat=0.42 TEST_DIR/t.qcow2 64M
-Invalid compatibility level: '0.42'
-qemu-img: TEST_DIR/t.qcow2: error while creating qcow2: Invalid argument
+qemu-img: TEST_DIR/t.qcow2: Invalid compatibility level: '0.42'
 Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.42' encryption=off cluster_size=65536 lazy_refcounts=off 
 
 qemu-img create -f qcow2 -o compat=foobar TEST_DIR/t.qcow2 64M
-Invalid compatibility level: 'foobar'
-qemu-img: TEST_DIR/t.qcow2: error while creating qcow2: Invalid argument
+qemu-img: TEST_DIR/t.qcow2: Invalid compatibility level: 'foobar'
 Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='foobar' encryption=off cluster_size=65536 lazy_refcounts=off 
 
 == Check preallocation option ==
@@ -181,8 +179,7 @@ qemu-img create -f qcow2 -o preallocation=metadata TEST_DIR/t.qcow2 64M
 Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 preallocation='metadata' lazy_refcounts=off 
 
 qemu-img create -f qcow2 -o preallocation=1234 TEST_DIR/t.qcow2 64M
-Invalid preallocation mode: '1234'
-qemu-img: TEST_DIR/t.qcow2: error while creating qcow2: Invalid argument
+qemu-img: TEST_DIR/t.qcow2: Invalid preallocation mode: '1234'
 Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 preallocation='1234' lazy_refcounts=off 
 
 == Check encryption option ==
@@ -205,8 +202,7 @@ qemu-img create -f qcow2 -o compat=0.10,lazy_refcounts=off TEST_DIR/t.qcow2 64M
 Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.10' encryption=off cluster_size=65536 lazy_refcounts=off 
 
 qemu-img create -f qcow2 -o compat=0.10,lazy_refcounts=on TEST_DIR/t.qcow2 64M
-Lazy refcounts only supported with compatibility level 1.1 and above (use compat=1.1 or greater)
-qemu-img: TEST_DIR/t.qcow2: error while creating qcow2: Invalid argument
+qemu-img: TEST_DIR/t.qcow2: Lazy refcounts only supported with compatibility level 1.1 and above (use compat=1.1 or greater)
 Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.10' encryption=off cluster_size=65536 lazy_refcounts=on 
 
 *** done
index 86e989cc6a8fd6ebbc25d5fea7bafa5451fa412f..88e8fa7de07f9f73609e10f73b87e800fc72ad93 100644 (file)
@@ -4,20 +4,16 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
 === Unknown option ===
 
 Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=
-QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt'
-QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=: could not open disk image TEST_DIR/t.qcow2: Invalid argument
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=: could not open disk image TEST_DIR/t.qcow2: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt'
 
 Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=on
-QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=on: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt'
-QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=on: could not open disk image TEST_DIR/t.qcow2: Invalid argument
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=on: could not open disk image TEST_DIR/t.qcow2: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt'
 
 Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=1234
-QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=1234: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt'
-QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=1234: could not open disk image TEST_DIR/t.qcow2: Invalid argument
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=1234: could not open disk image TEST_DIR/t.qcow2: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt'
 
 Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo
-QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt'
-QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo: could not open disk image TEST_DIR/t.qcow2: Invalid argument
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo: could not open disk image TEST_DIR/t.qcow2: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt'
 
 
 === Enable and disable lazy refcounting on the command line, plus some invalid values ===
@@ -31,24 +27,20 @@ QEMU X.Y.Z monitor - type 'help' for more information
 (qemu) q\e[K\e[Dqu\e[K\e[D\e[Dqui\e[K\e[D\e[D\e[Dquit\e[K\r
 
 Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=
-QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=: Parameter 'lazy-refcounts' expects 'on' or 'off'
-QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=: could not open disk image TEST_DIR/t.qcow2: Invalid argument
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=: could not open disk image TEST_DIR/t.qcow2: Parameter 'lazy-refcounts' expects 'on' or 'off'
 
 Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=42
-QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=42: Parameter 'lazy-refcounts' expects 'on' or 'off'
-QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=42: could not open disk image TEST_DIR/t.qcow2: Invalid argument
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=42: could not open disk image TEST_DIR/t.qcow2: Parameter 'lazy-refcounts' expects 'on' or 'off'
 
 Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=foo
-QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=foo: Parameter 'lazy-refcounts' expects 'on' or 'off'
-QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=foo: could not open disk image TEST_DIR/t.qcow2: Invalid argument
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=foo: could not open disk image TEST_DIR/t.qcow2: Parameter 'lazy-refcounts' expects 'on' or 'off'
 
 
 === With version 2 images enabling lazy refcounts must fail ===
 
 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 
 Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=on
-QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=on: Lazy refcounts require a qcow2 image with at least qemu 1.1 compatibility level
-QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=on: could not open disk image TEST_DIR/t.qcow2: Invalid argument
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=on: could not open disk image TEST_DIR/t.qcow2: Lazy refcounts require a qcow2 image with at least qemu 1.1 compatibility level
 
 Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=off
 QEMU X.Y.Z monitor - type 'help' for more information\r
@@ -208,21 +200,18 @@ QEMU X.Y.Z monitor - type 'help' for more information
 (qemu) q\e[K\e[Dqu\e[K\e[D\e[Dqui\e[K\e[D\e[D\e[Dquit\e[K\r
 
 Testing: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2
-QEMU_PROG: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2: Can't use 'qcow2' as a block driver for the protocol level
-QEMU_PROG: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2: could not open disk image TEST_DIR/t.qcow2: Invalid argument
+QEMU_PROG: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2: could not open disk image TEST_DIR/t.qcow2: Can't use 'qcow2' as a block driver for the protocol level
 
 
 === Parsing protocol from file name ===
 
 Testing: -hda foo:bar
-QEMU_PROG: -hda foo:bar: Unknown protocol
-QEMU_PROG: -hda foo:bar: could not open disk image foo:bar: No such file or directory
+QEMU_PROG: -hda foo:bar: could not open disk image foo:bar: Unknown protocol
 
 Testing: -drive file=foo:bar
-QEMU_PROG: -drive file=foo:bar: Unknown protocol
-QEMU_PROG: -drive file=foo:bar: could not open disk image foo:bar: No such file or directory
+QEMU_PROG: -drive file=foo:bar: could not open disk image foo:bar: Unknown protocol
 
 Testing: -drive file.filename=foo:bar
-QEMU_PROG: -drive file.filename=foo:bar: could not open disk image ide0-hd0: No such file or directory
+QEMU_PROG: -drive file.filename=foo:bar: could not open disk image ide0-hd0: Could not open 'foo:bar': No such file or directory
 
 *** done
index 2f357c271dcc4c17ce0dda42bc08557ef80f5b1f..7161d6e50b538e3a142f682b5dd4b5710ff3cc01 100644 (file)
@@ -1,10 +1,10 @@
 QA output created by 054
 
 creating too large image (1 EB)
-qemu-img: The image size is too large for file format 'qcow2' (try using a larger cluster size)
+qemu-img: TEST_DIR/t.IMGFMT: The image size is too large for file format 'IMGFMT' (try using a larger cluster size)
 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1152921504606846976 
 
 creating too large image (1 EB) using qcow2.py
 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 
-qemu-img: Could not open 'TEST_DIR/t.qcow2': File too large
+qemu-img: Could not open 'TEST_DIR/t.qcow2': Image is too big
 *** done
diff --git a/tests/qemu-iotests/057 b/tests/qemu-iotests/057
new file mode 100755 (executable)
index 0000000..9cdd582
--- /dev/null
@@ -0,0 +1,259 @@
+#!/usr/bin/env python
+#
+# Tests for internal snapshot.
+#
+# Copyright (C) 2013 IBM, Inc.
+#
+# Based on 055.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+import time
+import os
+import iotests
+from iotests import qemu_img, qemu_io
+
+test_drv_base_name = 'drive'
+
+class ImageSnapshotTestCase(iotests.QMPTestCase):
+    image_len = 120 * 1024 * 1024 # MB
+
+    def __init__(self, *args):
+        self.expect = []
+        super(ImageSnapshotTestCase, self).__init__(*args)
+
+    def _setUp(self, test_img_base_name, image_num):
+        self.vm = iotests.VM()
+        for i in range(0, image_num):
+            filename = '%s%d' % (test_img_base_name, i)
+            img = os.path.join(iotests.test_dir, filename)
+            device = '%s%d' % (test_drv_base_name, i)
+            qemu_img('create', '-f', iotests.imgfmt, img, str(self.image_len))
+            self.vm.add_drive(img)
+            self.expect.append({'image': img, 'device': device,
+                                'snapshots': [],
+                                'snapshots_name_counter': 0})
+        self.vm.launch()
+
+    def tearDown(self):
+        self.vm.shutdown()
+        for dev_expect in self.expect:
+            os.remove(dev_expect['image'])
+
+    def createSnapshotInTransaction(self, snapshot_num, abort = False):
+        actions = []
+        for dev_expect in self.expect:
+            num = dev_expect['snapshots_name_counter']
+            for j in range(0, snapshot_num):
+                name = '%s_sn%d' % (dev_expect['device'], num)
+                num = num + 1
+                if abort == False:
+                    dev_expect['snapshots'].append({'name': name})
+                    dev_expect['snapshots_name_counter'] = num
+                actions.append({
+                    'type': 'blockdev-snapshot-internal-sync',
+                    'data': { 'device': dev_expect['device'],
+                              'name': name },
+                })
+
+        if abort == True:
+            actions.append({
+                'type': 'abort',
+                'data': {},
+            })
+
+        result = self.vm.qmp('transaction', actions = actions)
+
+        if abort == True:
+            self.assert_qmp(result, 'error/class', 'GenericError')
+        else:
+            self.assert_qmp(result, 'return', {})
+
+    def verifySnapshotInfo(self):
+        result = self.vm.qmp('query-block')
+
+        # Verify each expected result
+        for dev_expect in self.expect:
+            # 1. Find the returned image value and snapshot info
+            image_result = None
+            for device in result['return']:
+                if device['device'] == dev_expect['device']:
+                    image_result = device['inserted']['image']
+                    break
+            self.assertTrue(image_result != None)
+            # Do not consider zero snapshot case now
+            sn_list_result = image_result['snapshots']
+            sn_list_expect = dev_expect['snapshots']
+
+            # 2. Verify it with expect
+            self.assertTrue(len(sn_list_result) == len(sn_list_expect))
+
+            for sn_expect in sn_list_expect:
+                sn_result = None
+                for sn in sn_list_result:
+                    if sn_expect['name'] == sn['name']:
+                        sn_result = sn
+                        break
+                self.assertTrue(sn_result != None)
+                # Fill in the detail info
+                sn_expect.update(sn_result)
+
+    def deleteSnapshot(self, device, id = None, name = None):
+        sn_list_expect = None
+        sn_expect = None
+
+        self.assertTrue(id != None or name != None)
+
+        # Fill in the detail info include ID
+        self.verifySnapshotInfo()
+
+        #find the expected snapshot list
+        for dev_expect in self.expect:
+            if dev_expect['device'] == device:
+                sn_list_expect = dev_expect['snapshots']
+                break
+        self.assertTrue(sn_list_expect != None)
+
+        if id != None and name != None:
+            for sn in sn_list_expect:
+                if sn['id'] == id and sn['name'] == name:
+                    sn_expect = sn
+                    result = \
+                          self.vm.qmp('blockdev-snapshot-delete-internal-sync',
+                                      device = device,
+                                      id = id,
+                                      name = name)
+                    break
+        elif id != None:
+            for sn in sn_list_expect:
+                if sn['id'] == id:
+                    sn_expect = sn
+                    result = \
+                          self.vm.qmp('blockdev-snapshot-delete-internal-sync',
+                                      device = device,
+                                      id = id)
+                    break
+        else:
+            for sn in sn_list_expect:
+                if sn['name'] == name:
+                    sn_expect = sn
+                    result = \
+                          self.vm.qmp('blockdev-snapshot-delete-internal-sync',
+                                      device = device,
+                                      name = name)
+                    break
+
+        self.assertTrue(sn_expect != None)
+
+        self.assert_qmp(result, 'return', sn_expect)
+        sn_list_expect.remove(sn_expect)
+
+class TestSingleTransaction(ImageSnapshotTestCase):
+    def setUp(self):
+        self._setUp('test_a.img', 1)
+
+    def test_create(self):
+        self.createSnapshotInTransaction(1)
+        self.verifySnapshotInfo()
+
+    def test_error_name_empty(self):
+        actions = [{'type': 'blockdev-snapshot-internal-sync',
+                    'data': { 'device': self.expect[0]['device'],
+                              'name': '' },
+                  }]
+        result = self.vm.qmp('transaction', actions = actions)
+        self.assert_qmp(result, 'error/class', 'GenericError')
+
+    def test_error_device(self):
+        actions = [{'type': 'blockdev-snapshot-internal-sync',
+                    'data': { 'device': 'drive_error',
+                              'name': 'a' },
+                  }]
+        result = self.vm.qmp('transaction', actions = actions)
+        self.assert_qmp(result, 'error/class', 'DeviceNotFound')
+
+    def test_error_exist(self):
+        self.createSnapshotInTransaction(1)
+        self.verifySnapshotInfo()
+        actions = [{'type': 'blockdev-snapshot-internal-sync',
+                    'data': { 'device': self.expect[0]['device'],
+                              'name': self.expect[0]['snapshots'][0] },
+                  }]
+        result = self.vm.qmp('transaction', actions = actions)
+        self.assert_qmp(result, 'error/class', 'GenericError')
+
+class TestMultipleTransaction(ImageSnapshotTestCase):
+    def setUp(self):
+        self._setUp('test_b.img', 2)
+
+    def test_create(self):
+        self.createSnapshotInTransaction(3)
+        self.verifySnapshotInfo()
+
+    def test_abort(self):
+        self.createSnapshotInTransaction(2)
+        self.verifySnapshotInfo()
+        self.createSnapshotInTransaction(3, abort = True)
+        self.verifySnapshotInfo()
+
+class TestSnapshotDelete(ImageSnapshotTestCase):
+    def setUp(self):
+        self._setUp('test_c.img', 1)
+
+    def test_delete_with_id(self):
+        self.createSnapshotInTransaction(2)
+        self.verifySnapshotInfo()
+        self.deleteSnapshot(self.expect[0]['device'],
+                            id = self.expect[0]['snapshots'][0]['id'])
+        self.verifySnapshotInfo()
+
+    def test_delete_with_name(self):
+        self.createSnapshotInTransaction(3)
+        self.verifySnapshotInfo()
+        self.deleteSnapshot(self.expect[0]['device'],
+                            name = self.expect[0]['snapshots'][1]['name'])
+        self.verifySnapshotInfo()
+
+    def test_delete_with_id_and_name(self):
+        self.createSnapshotInTransaction(4)
+        self.verifySnapshotInfo()
+        self.deleteSnapshot(self.expect[0]['device'],
+                            id = self.expect[0]['snapshots'][2]['id'],
+                            name = self.expect[0]['snapshots'][2]['name'])
+        self.verifySnapshotInfo()
+
+
+    def test_error_device(self):
+        result = self.vm.qmp('blockdev-snapshot-delete-internal-sync',
+                              device = 'drive_error',
+                              id = '0')
+        self.assert_qmp(result, 'error/class', 'DeviceNotFound')
+
+    def test_error_no_id_and_name(self):
+        result = self.vm.qmp('blockdev-snapshot-delete-internal-sync',
+                              device = self.expect[0]['device'])
+        self.assert_qmp(result, 'error/class', 'GenericError')
+
+    def test_error_snapshot_not_exist(self):
+        self.createSnapshotInTransaction(2)
+        self.verifySnapshotInfo()
+        result = self.vm.qmp('blockdev-snapshot-delete-internal-sync',
+                              device = self.expect[0]['device'],
+                              id = self.expect[0]['snapshots'][0]['id'],
+                              name = self.expect[0]['snapshots'][1]['name'])
+        self.assert_qmp(result, 'error/class', 'GenericError')
+
+if __name__ == '__main__':
+    iotests.main(supported_fmts=['qcow2'])
diff --git a/tests/qemu-iotests/057.out b/tests/qemu-iotests/057.out
new file mode 100644 (file)
index 0000000..281b69e
--- /dev/null
@@ -0,0 +1,5 @@
+............
+----------------------------------------------------------------------
+Ran 12 tests
+
+OK
index 65bb09f023cab83b6828ce0a23d6aeee6a0e9531..9bbc43b706afbc76a762a780e80c842de3ff75a7 100755 (executable)
@@ -71,7 +71,7 @@ $QEMU_IO -c "write -P 0x2a 0 512" "$TEST_IMG" | _filter_qemu_io
 ./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
 
 # Try to open the image R/W (which should fail)
-$QEMU_IO -c "read 0 512" "$TEST_IMG" 2>&1 | _filter_qemu_io | sed -e "s/can't open device .*$/can't open device/"
+$QEMU_IO -c "read 0 512" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir | _filter_imgfmt
 
 # Try to open it RO (which should succeed)
 $QEMU_IO -c "read 0 512" -r "$TEST_IMG" | _filter_qemu_io
index ca4583a4a4dbd71e96be86b4e682303dcea9518e..648f7437a268ed5991b8068d466d48fa446c5559 100644 (file)
@@ -11,8 +11,7 @@ incompatible_features     0x0
 qcow2: Preventing invalid write on metadata (overlaps with active L1 table); image marked as corrupt.
 write failed: Input/output error
 incompatible_features     0x2
-qcow2: Image is corrupt; cannot be opened read/write.
-qemu-io: can't open device
+qemu-io: can't open device TEST_DIR/t.IMGFMT: IMGFMT: Image is corrupt; cannot be opened read/write
 no file open, try 'help open'
 read 512/512 bytes at offset 0
 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
diff --git a/tests/qemu-iotests/061 b/tests/qemu-iotests/061
new file mode 100755 (executable)
index 0000000..5f04bfa
--- /dev/null
@@ -0,0 +1,206 @@
+#!/bin/bash
+#
+# Test case for image option amendment in qcow2.
+#
+# Copyright (C) 2013 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+# creator
+owner=mreitz@redhat.com
+
+seq=`basename $0`
+echo "QA output created by $seq"
+
+here=`pwd`
+tmp=/tmp/$$
+status=1       # failure is the default!
+
+_cleanup()
+{
+       _cleanup_test_img
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+# This tests qocw2-specific low-level functionality
+_supported_fmt qcow2
+_supported_proto generic
+_supported_os Linux
+
+echo
+echo "=== Testing version downgrade with zero expansion ==="
+echo
+IMGOPTS="compat=1.1,lazy_refcounts=on" _make_test_img 64M
+$QEMU_IO -c "write -z 0 128k" "$TEST_IMG" | _filter_qemu_io
+./qcow2.py "$TEST_IMG" dump-header
+$QEMU_IMG amend -o "compat=0.10" "$TEST_IMG"
+./qcow2.py "$TEST_IMG" dump-header
+$QEMU_IO -c "read -P 0 0 128k" "$TEST_IMG" | _filter_qemu_io
+_check_test_img
+
+echo
+echo "=== Testing dirty version downgrade ==="
+echo
+IMGOPTS="compat=1.1,lazy_refcounts=on" _make_test_img 64M
+$QEMU_IO -c "write -P 0x2a 0 128k" -c flush -c abort "$TEST_IMG" | _filter_qemu_io
+./qcow2.py "$TEST_IMG" dump-header
+$QEMU_IMG amend -o "compat=0.10" "$TEST_IMG"
+./qcow2.py "$TEST_IMG" dump-header
+$QEMU_IO -c "read -P 0x2a 0 128k" "$TEST_IMG" | _filter_qemu_io
+_check_test_img
+
+echo
+echo "=== Testing version downgrade with unknown compat/autoclear flags ==="
+echo
+IMGOPTS="compat=1.1" _make_test_img 64M
+./qcow2.py "$TEST_IMG" set-feature-bit compatible 42
+./qcow2.py "$TEST_IMG" set-feature-bit autoclear 42
+./qcow2.py "$TEST_IMG" dump-header
+$QEMU_IMG amend -o "compat=0.10" "$TEST_IMG"
+./qcow2.py "$TEST_IMG" dump-header
+_check_test_img
+
+echo
+echo "=== Testing version upgrade and resize ==="
+echo
+IMGOPTS="compat=0.10" _make_test_img 64M
+$QEMU_IO -c "write -P 0x2a 42M 64k" "$TEST_IMG" | _filter_qemu_io
+./qcow2.py "$TEST_IMG" dump-header
+$QEMU_IMG amend -o "compat=1.1,lazy_refcounts=on,size=128M" "$TEST_IMG"
+./qcow2.py "$TEST_IMG" dump-header
+$QEMU_IO -c "read -P 0x2a 42M 64k" "$TEST_IMG" | _filter_qemu_io
+_check_test_img
+
+echo
+echo "=== Testing dirty lazy_refcounts=off ==="
+echo
+IMGOPTS="compat=1.1,lazy_refcounts=on" _make_test_img 64M
+$QEMU_IO -c "write -P 0x2a 0 128k" -c flush -c abort "$TEST_IMG" | _filter_qemu_io
+./qcow2.py "$TEST_IMG" dump-header
+$QEMU_IMG amend -o "lazy_refcounts=off" "$TEST_IMG"
+./qcow2.py "$TEST_IMG" dump-header
+$QEMU_IO -c "read -P 0x2a 0 128k" "$TEST_IMG" | _filter_qemu_io
+_check_test_img
+
+echo
+echo "=== Testing backing file ==="
+echo
+IMGOPTS="compat=1.1" _make_test_img 64M
+IMGOPTS="compat=1.1" TEST_IMG="$TEST_IMG.base" _make_test_img 64M
+$QEMU_IO -c "write -P 0x2a 0 128k" "$TEST_IMG.base" | _filter_qemu_io
+$QEMU_IO -c "read -P 0 0 128k" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IMG amend -o "backing_file=$TEST_IMG.base,backing_fmt=qcow2" "$TEST_IMG"
+$QEMU_IO -c "read -P 0x2a 0 128k" "$TEST_IMG" | _filter_qemu_io
+_check_test_img
+
+echo
+echo "=== Testing invalid configurations ==="
+echo
+IMGOPTS="compat=0.10" _make_test_img 64M
+$QEMU_IMG amend -o "lazy_refcounts=on" "$TEST_IMG"
+$QEMU_IMG amend -o "compat=1.1" "$TEST_IMG" # actually valid
+$QEMU_IMG amend -o "compat=0.10,lazy_refcounts=on" "$TEST_IMG"
+$QEMU_IMG amend -o "compat=0.42" "$TEST_IMG"
+$QEMU_IMG amend -o "foo=bar" "$TEST_IMG"
+$QEMU_IMG amend -o "cluster_size=1k" "$TEST_IMG"
+$QEMU_IMG amend -o "encryption=on" "$TEST_IMG"
+$QEMU_IMG amend -o "preallocation=on" "$TEST_IMG"
+
+echo
+echo "=== Testing correct handling of unset value ==="
+echo
+IMGOPTS="compat=1.1,cluster_size=1k" _make_test_img 64M
+echo "Should work:"
+$QEMU_IMG amend -o "lazy_refcounts=on" "$TEST_IMG"
+echo "Should not work:" # Just to know which of these tests actually fails
+$QEMU_IMG amend -o "cluster_size=64k" "$TEST_IMG"
+
+echo
+echo "=== Testing zero expansion on inactive clusters ==="
+echo
+IMGOPTS="compat=1.1" _make_test_img 64M
+$QEMU_IO -c "write -z 0 128k" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IMG snapshot -c foo "$TEST_IMG"
+$QEMU_IO -c "write -P 0x2a 0 128k" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IMG amend -o "compat=0.10" "$TEST_IMG"
+_check_test_img
+$QEMU_IO -c "read -P 0x2a 0 128k" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IMG snapshot -a foo "$TEST_IMG"
+_check_test_img
+$QEMU_IO -c "read -P 0 0 128k" "$TEST_IMG" | _filter_qemu_io
+
+echo
+echo "=== Testing zero expansion on shared L2 table ==="
+echo
+IMGOPTS="compat=1.1" _make_test_img 64M
+$QEMU_IO -c "write -z 0 128k" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IMG snapshot -c foo "$TEST_IMG"
+$QEMU_IMG amend -o "compat=0.10" "$TEST_IMG"
+_check_test_img
+$QEMU_IO -c "read -P 0 0 128k" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IMG snapshot -a foo "$TEST_IMG"
+_check_test_img
+$QEMU_IO -c "read -P 0 0 128k" "$TEST_IMG" | _filter_qemu_io
+
+echo
+echo "=== Testing zero expansion on backed image ==="
+echo
+IMGOPTS="compat=1.1" TEST_IMG="$TEST_IMG.base" _make_test_img 64M
+$QEMU_IO -c "write -P 0x2a 0 128k" "$TEST_IMG.base" | _filter_qemu_io
+IMGOPTS="compat=1.1,backing_file=$TEST_IMG.base" _make_test_img 64M
+$QEMU_IO -c "read -P 0x2a 0 128k" -c "write -z 0 64k" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IMG amend -o "compat=0.10" "$TEST_IMG"
+_check_test_img
+$QEMU_IO -c "read -P 0 0 64k" -c "read -P 0x2a 64k 64k" "$TEST_IMG" | _filter_qemu_io
+
+echo
+echo "=== Testing zero expansion on backed inactive clusters ==="
+echo
+IMGOPTS="compat=1.1" TEST_IMG="$TEST_IMG.base" _make_test_img 64M
+$QEMU_IO -c "write -P 0x2a 0 128k" "$TEST_IMG.base" | _filter_qemu_io
+IMGOPTS="compat=1.1,backing_file=$TEST_IMG.base" _make_test_img 64M
+$QEMU_IO -c "write -z 0 64k" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IMG snapshot -c foo "$TEST_IMG"
+$QEMU_IO -c "write -P 0x42 0 128k" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IMG amend -o "compat=0.10" "$TEST_IMG"
+_check_test_img
+$QEMU_IO -c "read -P 0x42 0 128k" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IMG snapshot -a foo "$TEST_IMG"
+_check_test_img
+$QEMU_IO -c "read -P 0 0 64k" -c "read -P 0x2a 64k 64k" "$TEST_IMG" | _filter_qemu_io
+
+echo
+echo "=== Testing zero expansion on backed image with shared L2 table ==="
+echo
+IMGOPTS="compat=1.1" TEST_IMG="$TEST_IMG.base" _make_test_img 64M
+$QEMU_IO -c "write -P 0x2a 0 128k" "$TEST_IMG.base" | _filter_qemu_io
+IMGOPTS="compat=1.1,backing_file=$TEST_IMG.base" _make_test_img 64M
+$QEMU_IO -c "write -z 0 128k" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IMG snapshot -c foo "$TEST_IMG"
+$QEMU_IMG amend -o "compat=0.10" "$TEST_IMG"
+_check_test_img
+$QEMU_IO -c "read -P 0 0 128k" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IMG snapshot -a foo "$TEST_IMG"
+_check_test_img
+$QEMU_IO -c "read -P 0 0 128k" "$TEST_IMG" | _filter_qemu_io
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/061.out b/tests/qemu-iotests/061.out
new file mode 100644 (file)
index 0000000..d42127f
--- /dev/null
@@ -0,0 +1,376 @@
+QA output created by 061
+
+=== Testing version downgrade with zero expansion ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 
+wrote 131072/131072 bytes at offset 0
+128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+magic                     0x514649fb
+version                   3
+backing_file_offset       0x0
+backing_file_size         0x0
+cluster_bits              16
+size                      67108864
+crypt_method              0
+l1_size                   1
+l1_table_offset           0x30000
+refcount_table_offset     0x10000
+refcount_table_clusters   1
+nb_snapshots              0
+snapshot_offset           0x0
+incompatible_features     0x0
+compatible_features       0x1
+autoclear_features        0x0
+refcount_order            4
+header_length             104
+
+magic                     0x514649fb
+version                   2
+backing_file_offset       0x0
+backing_file_size         0x0
+cluster_bits              16
+size                      67108864
+crypt_method              0
+l1_size                   1
+l1_table_offset           0x30000
+refcount_table_offset     0x10000
+refcount_table_clusters   1
+nb_snapshots              0
+snapshot_offset           0x0
+incompatible_features     0x0
+compatible_features       0x0
+autoclear_features        0x0
+refcount_order            4
+header_length             72
+
+Header extension:
+magic                     0x6803f857
+length                    144
+data                      <binary>
+
+read 131072/131072 bytes at offset 0
+128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+No errors were found on the image.
+
+=== Testing dirty version downgrade ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 
+wrote 131072/131072 bytes at offset 0
+128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+magic                     0x514649fb
+version                   3
+backing_file_offset       0x0
+backing_file_size         0x0
+cluster_bits              16
+size                      67108864
+crypt_method              0
+l1_size                   1
+l1_table_offset           0x30000
+refcount_table_offset     0x10000
+refcount_table_clusters   1
+nb_snapshots              0
+snapshot_offset           0x0
+incompatible_features     0x1
+compatible_features       0x1
+autoclear_features        0x0
+refcount_order            4
+header_length             104
+
+Repairing cluster 5 refcount=0 reference=1
+Repairing cluster 6 refcount=0 reference=1
+magic                     0x514649fb
+version                   2
+backing_file_offset       0x0
+backing_file_size         0x0
+cluster_bits              16
+size                      67108864
+crypt_method              0
+l1_size                   1
+l1_table_offset           0x30000
+refcount_table_offset     0x10000
+refcount_table_clusters   1
+nb_snapshots              0
+snapshot_offset           0x0
+incompatible_features     0x0
+compatible_features       0x0
+autoclear_features        0x0
+refcount_order            4
+header_length             72
+
+Header extension:
+magic                     0x6803f857
+length                    144
+data                      <binary>
+
+read 131072/131072 bytes at offset 0
+128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+No errors were found on the image.
+
+=== Testing version downgrade with unknown compat/autoclear flags ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 
+magic                     0x514649fb
+version                   3
+backing_file_offset       0x0
+backing_file_size         0x0
+cluster_bits              16
+size                      67108864
+crypt_method              0
+l1_size                   1
+l1_table_offset           0x30000
+refcount_table_offset     0x10000
+refcount_table_clusters   1
+nb_snapshots              0
+snapshot_offset           0x0
+incompatible_features     0x0
+compatible_features       0x40000000000
+autoclear_features        0x40000000000
+refcount_order            4
+header_length             104
+
+magic                     0x514649fb
+version                   2
+backing_file_offset       0x0
+backing_file_size         0x0
+cluster_bits              16
+size                      67108864
+crypt_method              0
+l1_size                   1
+l1_table_offset           0x30000
+refcount_table_offset     0x10000
+refcount_table_clusters   1
+nb_snapshots              0
+snapshot_offset           0x0
+incompatible_features     0x0
+compatible_features       0x0
+autoclear_features        0x0
+refcount_order            4
+header_length             72
+
+Header extension:
+magic                     0x6803f857
+length                    144
+data                      <binary>
+
+No errors were found on the image.
+
+=== Testing version upgrade and resize ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 
+wrote 65536/65536 bytes at offset 44040192
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+magic                     0x514649fb
+version                   2
+backing_file_offset       0x0
+backing_file_size         0x0
+cluster_bits              16
+size                      67108864
+crypt_method              0
+l1_size                   1
+l1_table_offset           0x30000
+refcount_table_offset     0x10000
+refcount_table_clusters   1
+nb_snapshots              0
+snapshot_offset           0x0
+incompatible_features     0x0
+compatible_features       0x0
+autoclear_features        0x0
+refcount_order            4
+header_length             72
+
+magic                     0x514649fb
+version                   3
+backing_file_offset       0x0
+backing_file_size         0x0
+cluster_bits              16
+size                      134217728
+crypt_method              0
+l1_size                   1
+l1_table_offset           0x30000
+refcount_table_offset     0x10000
+refcount_table_clusters   1
+nb_snapshots              0
+snapshot_offset           0x0
+incompatible_features     0x0
+compatible_features       0x1
+autoclear_features        0x0
+refcount_order            4
+header_length             104
+
+Header extension:
+magic                     0x6803f857
+length                    144
+data                      <binary>
+
+read 65536/65536 bytes at offset 44040192
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+No errors were found on the image.
+
+=== Testing dirty lazy_refcounts=off ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 
+wrote 131072/131072 bytes at offset 0
+128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+magic                     0x514649fb
+version                   3
+backing_file_offset       0x0
+backing_file_size         0x0
+cluster_bits              16
+size                      67108864
+crypt_method              0
+l1_size                   1
+l1_table_offset           0x30000
+refcount_table_offset     0x10000
+refcount_table_clusters   1
+nb_snapshots              0
+snapshot_offset           0x0
+incompatible_features     0x1
+compatible_features       0x1
+autoclear_features        0x0
+refcount_order            4
+header_length             104
+
+Repairing cluster 5 refcount=0 reference=1
+Repairing cluster 6 refcount=0 reference=1
+magic                     0x514649fb
+version                   3
+backing_file_offset       0x0
+backing_file_size         0x0
+cluster_bits              16
+size                      67108864
+crypt_method              0
+l1_size                   1
+l1_table_offset           0x30000
+refcount_table_offset     0x10000
+refcount_table_clusters   1
+nb_snapshots              0
+snapshot_offset           0x0
+incompatible_features     0x0
+compatible_features       0x0
+autoclear_features        0x0
+refcount_order            4
+header_length             104
+
+Header extension:
+magic                     0x6803f857
+length                    144
+data                      <binary>
+
+read 131072/131072 bytes at offset 0
+128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+No errors were found on the image.
+
+=== Testing backing file ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 
+wrote 131072/131072 bytes at offset 0
+128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 131072/131072 bytes at offset 0
+128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 131072/131072 bytes at offset 0
+128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+No errors were found on the image.
+
+=== Testing invalid configurations ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 
+Lazy refcounts only supported with compatibility level 1.1 and above (use compat=1.1 or greater)
+qemu-img: Error while amending options: Invalid argument
+Lazy refcounts only supported with compatibility level 1.1 and above (use compat=1.1 or greater)
+qemu-img: Error while amending options: Invalid argument
+Unknown compatibility level 0.42.
+qemu-img: Error while amending options: Invalid argument
+Unknown option 'foo'
+qemu-img: Invalid options for file format 'qcow2'
+Changing the cluster size is not supported.
+qemu-img: Error while amending options: Operation not supported
+Changing the encryption flag is not supported.
+qemu-img: Error while amending options: Operation not supported
+Cannot change preallocation mode.
+qemu-img: Error while amending options: Operation not supported
+
+=== Testing correct handling of unset value ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 
+Should work:
+Should not work:
+Changing the cluster size is not supported.
+qemu-img: Error while amending options: Operation not supported
+
+=== Testing zero expansion on inactive clusters ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 
+wrote 131072/131072 bytes at offset 0
+128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 131072/131072 bytes at offset 0
+128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+No errors were found on the image.
+read 131072/131072 bytes at offset 0
+128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+No errors were found on the image.
+read 131072/131072 bytes at offset 0
+128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== Testing zero expansion on shared L2 table ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 
+wrote 131072/131072 bytes at offset 0
+128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+No errors were found on the image.
+read 131072/131072 bytes at offset 0
+128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+No errors were found on the image.
+read 131072/131072 bytes at offset 0
+128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== Testing zero expansion on backed image ===
+
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 
+wrote 131072/131072 bytes at offset 0
+128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' 
+read 131072/131072 bytes at offset 0
+128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+No errors were found on the image.
+read 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 65536/65536 bytes at offset 65536
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== Testing zero expansion on backed inactive clusters ===
+
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 
+wrote 131072/131072 bytes at offset 0
+128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' 
+wrote 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 131072/131072 bytes at offset 0
+128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+No errors were found on the image.
+read 131072/131072 bytes at offset 0
+128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+No errors were found on the image.
+read 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 65536/65536 bytes at offset 65536
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== Testing zero expansion on backed image with shared L2 table ===
+
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 
+wrote 131072/131072 bytes at offset 0
+128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base' 
+wrote 131072/131072 bytes at offset 0
+128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+No errors were found on the image.
+read 131072/131072 bytes at offset 0
+128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+No errors were found on the image.
+read 131072/131072 bytes at offset 0
+128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+*** done
index 4ecf497d8e39dd034e3dd173f704f1533977eec6..f5f328f5f50677bce263ba04b777a76da3280f74 100755 (executable)
@@ -164,6 +164,7 @@ QEMU_IO       -- $QEMU_IO
 IMGFMT        -- $FULL_IMGFMT_DETAILS
 IMGPROTO      -- $FULL_IMGPROTO_DETAILS
 PLATFORM      -- $FULL_HOST_DETAILS
+SOCKET_SCM_HELPER -- $SOCKET_SCM_HELPER
 
 EOF
 #MKFS_OPTIONS  -- $FULL_MKFS_OPTIONS
index 88fecf7870fb33fb2a03624d214186e7172ee960..28b39e429edf69bb3c9449b047a77aebeb0c05af 100644 (file)
@@ -123,7 +123,7 @@ _make_test_img()
     fi
 
     # XXX(hch): have global image options?
-    $QEMU_IMG create -f $IMGFMT $extra_img_options $img_name $image_size | \
+    $QEMU_IMG create -f $IMGFMT $extra_img_options $img_name $image_size 2>&1 | \
         sed -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \
             -e "s#$TEST_DIR#TEST_DIR#g" \
             -e "s#$IMGFMT#IMGFMT#g" \
index 316b1dd75c8cd40b273b5d57a2d184ad538ca6f5..1ad02e5a2c5f2cf2c1224ac61bb267001cf75aae 100644 (file)
@@ -63,7 +63,9 @@
 054 rw auto
 055 rw auto
 056 rw auto backing
+057 rw auto
 059 rw auto
 060 rw auto
+061 rw auto
 062 rw auto
 063 rw auto
index 33ad0ecb920d4a8739cf410023dbf89af2e3fe0c..87b4a3a880e103d9d72bbd537873ca73f391b158 100644 (file)
@@ -38,6 +38,8 @@ imgfmt = os.environ.get('IMGFMT', 'raw')
 imgproto = os.environ.get('IMGPROTO', 'file')
 test_dir = os.environ.get('TEST_DIR', '/var/tmp')
 
+socket_scm_helper = os.environ.get('SOCKET_SCM_HELPER', 'socket_scm_helper')
+
 def qemu_img(*args):
     '''Run qemu-img and return the exit code'''
     devnull = open('/dev/null', 'r+')
@@ -80,6 +82,12 @@ class VM(object):
                      '-display', 'none', '-vga', 'none']
         self._num_drives = 0
 
+    # This can be used to add an unused monitor instance.
+    def add_monitor_telnet(self, ip, port):
+        args = 'tcp:%s:%d,server,nowait,telnet' % (ip, port)
+        self._args.append('-monitor')
+        self._args.append(args)
+
     def add_drive(self, path, opts=''):
         '''Add a virtio-blk drive to the VM'''
         options = ['if=virtio',
@@ -112,6 +120,21 @@ class VM(object):
         self._args.append(','.join(options))
         return self
 
+    def send_fd_scm(self, fd_file_path):
+        # In iotest.py, the qmp should always use unix socket.
+        assert self._qmp.is_scm_available()
+        bin = socket_scm_helper
+        if os.path.exists(bin) == False:
+            print "Scm help program does not present, path '%s'." % bin
+            return -1
+        fd_param = ["%s" % bin,
+                    "%d" % self._qmp.get_sock_fd(),
+                    "%s" % fd_file_path]
+        devnull = open('/dev/null', 'rb')
+        p = subprocess.Popen(fd_param, stdin=devnull, stdout=sys.stdout,
+                             stderr=sys.stderr)
+        return p.wait()
+
     def launch(self):
         '''Launch the VM and establish a QMP connection'''
         devnull = open('/dev/null', 'rb')
diff --git a/tests/qemu-iotests/socket_scm_helper.c b/tests/qemu-iotests/socket_scm_helper.c
new file mode 100644 (file)
index 0000000..0e2b285
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * SCM_RIGHTS with unix socket help program for test
+ *
+ * Copyright IBM, Inc. 2013
+ *
+ * Authors:
+ *  Wenchao Xia    <xiawenc@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+/* #define SOCKET_SCM_DEBUG */
+
+/*
+ * @fd and @fd_to_send will not be checked for validation in this function,
+ * a blank will be sent as iov data to notify qemu.
+ */
+static int send_fd(int fd, int fd_to_send)
+{
+    struct msghdr msg;
+    struct iovec iov[1];
+    int ret;
+    char control[CMSG_SPACE(sizeof(int))];
+    struct cmsghdr *cmsg;
+
+    memset(&msg, 0, sizeof(msg));
+    memset(control, 0, sizeof(control));
+
+    /* Send a blank to notify qemu */
+    iov[0].iov_base = (void *)" ";
+    iov[0].iov_len = 1;
+
+    msg.msg_iov = iov;
+    msg.msg_iovlen = 1;
+
+    msg.msg_control = control;
+    msg.msg_controllen = sizeof(control);
+
+    cmsg = CMSG_FIRSTHDR(&msg);
+
+    cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+    cmsg->cmsg_level = SOL_SOCKET;
+    cmsg->cmsg_type = SCM_RIGHTS;
+    memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
+
+    do {
+        ret = sendmsg(fd, &msg, 0);
+    } while (ret < 0 && errno == EINTR);
+
+    if (ret < 0) {
+        fprintf(stderr, "Failed to send msg, reason: %s\n", strerror(errno));
+    }
+
+    return ret;
+}
+
+/* Convert string to fd number. */
+static int get_fd_num(const char *fd_str)
+{
+    int sock;
+    char *err;
+
+    errno = 0;
+    sock = strtol(fd_str, &err, 10);
+    if (errno) {
+        fprintf(stderr, "Failed in strtol for socket fd, reason: %s\n",
+                strerror(errno));
+        return -1;
+    }
+    if (!*fd_str || *err || sock < 0) {
+        fprintf(stderr, "bad numerical value for socket fd '%s'\n", fd_str);
+        return -1;
+    }
+
+    return sock;
+}
+
+/*
+ * To make things simple, the caller needs to specify:
+ * 1. socket fd.
+ * 2. path of the file to be sent.
+ */
+int main(int argc, char **argv, char **envp)
+{
+    int sock, fd, ret;
+
+#ifdef SOCKET_SCM_DEBUG
+    int i;
+    for (i = 0; i < argc; i++) {
+        fprintf(stderr, "Parameter %d: %s\n", i, argv[i]);
+    }
+#endif
+
+    if (argc != 3) {
+        fprintf(stderr,
+                "Usage: %s < socket-fd > < file-path >\n",
+                argv[0]);
+        return EXIT_FAILURE;
+    }
+
+
+    sock = get_fd_num(argv[1]);
+    if (sock < 0) {
+        return EXIT_FAILURE;
+    }
+
+    /* Now only open a file in readonly mode for test purpose. If more precise
+       control is needed, use python script in file operation, which is
+       supposed to fork and exec this program. */
+    fd = open(argv[2], O_RDONLY);
+    if (fd < 0) {
+        fprintf(stderr, "Failed to open file '%s'\n", argv[2]);
+        return EXIT_FAILURE;
+    }
+
+    ret = send_fd(sock, fd);
+    if (ret < 0) {
+        close(fd);
+        return EXIT_FAILURE;
+    }
+
+    close(fd);
+    return EXIT_SUCCESS;
+}
index 3dc8b1b0743192aa929af70e85746b5b2106041d..253bc3df2e899ccf9e4a7e1cde9c5886b86dd877 100644 (file)
@@ -112,9 +112,7 @@ void *qemu_anon_ram_alloc(size_t size)
     size_t offset = QEMU_ALIGN_UP((uintptr_t)ptr, align) - (uintptr_t)ptr;
 
     if (ptr == MAP_FAILED) {
-        fprintf(stderr, "Failed to allocate %zu B: %s\n",
-                size, strerror(errno));
-        abort();
+        return NULL;
     }
 
     ptr += offset;
index 961fbf5e3de5d35d77c97814adf6f97762bd357c..983b7a2375808608c6dc16a604b33fffafa2c942 100644 (file)
@@ -65,10 +65,7 @@ void *qemu_anon_ram_alloc(size_t size)
     /* FIXME: this is not exactly optimal solution since VirtualAlloc
        has 64Kb granularity, but at least it guarantees us that the
        memory is page aligned. */
-    if (!size) {
-        abort();
-    }
-    ptr = qemu_oom_check(VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE));
+    ptr = VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
     trace_qemu_anon_ram_alloc(size, ptr);
     return ptr;
 }