* Speed up AddressSpaceDispatch creation (Alexey)

* Fix kvm.c assert (David)
 * Memory fixes and further speedup (me)
 * Persistent reservation manager infrastructure (me)
 * virtio-serial: add enable_backend callback (Pavel)
 * chardev GMainContext fixes (Peter)
 -----BEGIN PGP SIGNATURE-----
 
 iQFIBAABCAAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAlnFX3UUHHBib256aW5p
 QHJlZGhhdC5jb20ACgkQv/vSX3jHroMq2wf/Z7i67tTQYhaY7trAehdGDLSa6C4m
 0xAex+DVJrpfxFHLINkktx9NpvyZbQ/PuA0+5W10qmfPVF3hddTgLL3Dcg5xkQOh
 qNa2pFPMTn2T4eEdAANycNEF3nz8at5EnZ5anW2uMS41iDMq6aBjPhDgvi/iyG4w
 GBeZFjUUXQ8Wtp5fZJ1RgV/2PFg3W1REodvM143Ge84UUmnltf/snmx3NMQWw5wu
 coZFSIpcachMRxZ+bbLtJnCoRWG+8lkmTXYkswRWGez+WniscR0898RRpD0lJgIA
 cgeX5Cg/EbBIpwcqjsW2018WlsH5qp4rb6wVuqTY2kzbG+FUyKSqxSwGZw==
 =9GLQ
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/bonzini/tags/for-upstream' into staging

* Speed up AddressSpaceDispatch creation (Alexey)
* Fix kvm.c assert (David)
* Memory fixes and further speedup (me)
* Persistent reservation manager infrastructure (me)
* virtio-serial: add enable_backend callback (Pavel)
* chardev GMainContext fixes (Peter)

# gpg: Signature made Fri 22 Sep 2017 20:07:33 BST
# gpg:                using RSA key 0xBFFBD25F78C7AE83
# gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>"
# gpg:                 aka "Paolo Bonzini <pbonzini@redhat.com>"
# Primary key fingerprint: 46F5 9FBD 57D6 12E7 BFD4  E2F7 7E15 100C CD36 69B1
#      Subkey fingerprint: F133 3857 4B66 2389 866C  7682 BFFB D25F 78C7 AE83

* remotes/bonzini/tags/for-upstream: (32 commits)
  chardev: remove context in chr_update_read_handler
  chardev: use per-dev context for io_add_watch_poll
  chardev: add Chardev.gcontext field
  chardev: new qemu_chr_be_update_read_handlers()
  scsi: add persistent reservation manager using qemu-pr-helper
  scsi: add multipath support to qemu-pr-helper
  scsi: build qemu-pr-helper
  scsi, file-posix: add support for persistent reservation management
  memory: Share special empty FlatView
  memory: seek FlatView sharing candidates among children subregions
  memory: trace FlatView creation and destruction
  memory: Create FlatView directly
  memory: Get rid of address_space_init_shareable
  memory: Rework "info mtree" to print flat views and dispatch trees
  memory: Do not allocate FlatView in address_space_init
  memory: Share FlatView's and dispatch trees between address spaces
  memory: Move address_space_update_ioeventfds
  memory: Alloc dispatch tree where topology is generared
  memory: Store physical root MR in FlatView
  memory: Rename mem_begin/mem_commit/mem_add helpers
  ...

# Conflicts:
#	configure
This commit is contained in:
Peter Maydell 2017-09-23 12:55:40 +01:00
commit 460b6c8e58
44 changed files with 2552 additions and 312 deletions

View File

@ -372,6 +372,11 @@ qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o $(COMMON_LDADDS)
fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/9p-marshal.o fsdev/9p-iov-marshal.o $(COMMON_LDADDS)
fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap
scsi/qemu-pr-helper$(EXESUF): scsi/qemu-pr-helper.o scsi/utils.o $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
ifdef CONFIG_MPATH
scsi/qemu-pr-helper$(EXESUF): LIBS += -ludev -lmultipath -lmpathpersist
endif
qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.hx $(SRC_PATH)/scripts/hxtool
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@,"GEN","$@")
@ -488,7 +493,7 @@ clean:
rm -f *.msi
find . \( -name '*.so' -o -name '*.dll' -o -name '*.mo' -o -name '*.[oda]' \) -type f -exec rm {} +
rm -f $(filter-out %.tlb,$(TOOLS)) $(HELPERS-y) qemu-ga TAGS cscope.* *.pod *~ */*~
rm -f fsdev/*.pod
rm -f fsdev/*.pod scsi/*.pod
rm -f qemu-img-cmds.h
rm -f ui/shader/*-vert.h ui/shader/*-frag.h
@# May not be present in GENERATED_FILES

View File

@ -171,6 +171,7 @@ trace-events-subdirs += qapi
trace-events-subdirs += accel/tcg
trace-events-subdirs += accel/kvm
trace-events-subdirs += nbd
trace-events-subdirs += scsi
trace-events-files = $(SRC_PATH)/trace-events $(trace-events-subdirs:%=$(SRC_PATH)/%/trace-events)

View File

@ -722,7 +722,6 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
mem = kvm_lookup_matching_slot(kml, start_addr, size);
if (!add) {
if (!mem) {
g_assert(!memory_region_is_ram(mr) && !writeable && !mr->romd_mode);
return;
}
if (mem->flags & KVM_MEM_LOG_DIRTY_PAGES) {

View File

@ -33,6 +33,9 @@
#include "block/raw-aio.h"
#include "qapi/qmp/qstring.h"
#include "scsi/pr-manager.h"
#include "scsi/constants.h"
#if defined(__APPLE__) && (__MACH__)
#include <paths.h>
#include <sys/param.h>
@ -155,6 +158,8 @@ typedef struct BDRVRawState {
bool page_cache_inconsistent:1;
bool has_fallocate;
bool needs_alignment;
PRManager *pr_mgr;
} BDRVRawState;
typedef struct BDRVRawReopenState {
@ -402,6 +407,11 @@ static QemuOptsList raw_runtime_opts = {
.type = QEMU_OPT_STRING,
.help = "file locking mode (on/off/auto, default: auto)",
},
{
.name = "pr-manager",
.type = QEMU_OPT_STRING,
.help = "id of persistent reservation manager object (default: none)",
},
{ /* end of list */ }
},
};
@ -413,6 +423,7 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
QemuOpts *opts;
Error *local_err = NULL;
const char *filename = NULL;
const char *str;
BlockdevAioOptions aio, aio_default;
int fd, ret;
struct stat st;
@ -476,6 +487,16 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
abort();
}
str = qemu_opt_get(opts, "pr-manager");
if (str) {
s->pr_mgr = pr_manager_lookup(str, &local_err);
if (local_err) {
error_propagate(errp, local_err);
ret = -EINVAL;
goto fail;
}
}
s->open_flags = open_flags;
raw_parse_flags(bdrv_flags, &s->open_flags);
@ -2597,6 +2618,15 @@ static BlockAIOCB *hdev_aio_ioctl(BlockDriverState *bs,
if (fd_open(bs) < 0)
return NULL;
if (req == SG_IO && s->pr_mgr) {
struct sg_io_hdr *io_hdr = buf;
if (io_hdr->cmdp[0] == PERSISTENT_RESERVE_OUT ||
io_hdr->cmdp[0] == PERSISTENT_RESERVE_IN) {
return pr_manager_execute(s->pr_mgr, bdrv_get_aio_context(bs),
s->fd, io_hdr, cb, opaque);
}
}
acb = g_new(RawPosixAIOData, 1);
acb->bs = bs;
acb->aio_type = QEMU_AIO_IOCTL;

View File

@ -84,8 +84,7 @@ static GSource *fd_chr_add_watch(Chardev *chr, GIOCondition cond)
return qio_channel_create_watch(s->ioc_out, cond);
}
static void fd_chr_update_read_handler(Chardev *chr,
GMainContext *context)
static void fd_chr_update_read_handler(Chardev *chr)
{
FDChardev *s = FD_CHARDEV(chr);
@ -94,7 +93,7 @@ static void fd_chr_update_read_handler(Chardev *chr,
chr->gsource = io_add_watch_poll(chr, s->ioc_in,
fd_chr_read_poll,
fd_chr_read, chr,
context);
chr->gcontext);
}
}

View File

@ -253,7 +253,6 @@ void qemu_chr_fe_set_handlers(CharBackend *b,
bool set_open)
{
Chardev *s;
ChardevClass *cc;
int fe_open;
s = b->chr;
@ -261,7 +260,6 @@ void qemu_chr_fe_set_handlers(CharBackend *b,
return;
}
cc = CHARDEV_GET_CLASS(s);
if (!opaque && !fd_can_read && !fd_read && !fd_event) {
fe_open = 0;
remove_fd_in_watch(s);
@ -273,9 +271,8 @@ void qemu_chr_fe_set_handlers(CharBackend *b,
b->chr_event = fd_event;
b->chr_be_change = be_change;
b->opaque = opaque;
if (cc->chr_update_read_handler) {
cc->chr_update_read_handler(s, context);
}
qemu_chr_be_update_read_handlers(s, context);
if (set_open) {
qemu_chr_fe_set_open(b, fe_open);

View File

@ -112,8 +112,7 @@ static void pty_chr_update_read_handler_locked(Chardev *chr)
}
}
static void pty_chr_update_read_handler(Chardev *chr,
GMainContext *context)
static void pty_chr_update_read_handler(Chardev *chr)
{
qemu_mutex_lock(&chr->chr_write_lock);
pty_chr_update_read_handler_locked(chr);
@ -219,7 +218,7 @@ static void pty_chr_state(Chardev *chr, int connected)
chr->gsource = io_add_watch_poll(chr, s->ioc,
pty_chr_read_poll,
pty_chr_read,
chr, NULL);
chr, chr->gcontext);
}
}
}

View File

@ -516,13 +516,12 @@ static void tcp_chr_connect(void *opaque)
chr->gsource = io_add_watch_poll(chr, s->ioc,
tcp_chr_read_poll,
tcp_chr_read,
chr, NULL);
chr, chr->gcontext);
}
qemu_chr_be_event(chr, CHR_EVENT_OPENED);
}
static void tcp_chr_update_read_handler(Chardev *chr,
GMainContext *context)
static void tcp_chr_update_read_handler(Chardev *chr)
{
SocketChardev *s = SOCKET_CHARDEV(chr);
@ -535,7 +534,7 @@ static void tcp_chr_update_read_handler(Chardev *chr,
chr->gsource = io_add_watch_poll(chr, s->ioc,
tcp_chr_read_poll,
tcp_chr_read, chr,
context);
chr->gcontext);
}
}

View File

@ -100,8 +100,7 @@ static gboolean udp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
return TRUE;
}
static void udp_chr_update_read_handler(Chardev *chr,
GMainContext *context)
static void udp_chr_update_read_handler(Chardev *chr)
{
UdpChardev *s = UDP_CHARDEV(chr);
@ -110,7 +109,7 @@ static void udp_chr_update_read_handler(Chardev *chr,
chr->gsource = io_add_watch_poll(chr, s->ioc,
udp_chr_read_poll,
udp_chr_read, chr,
context);
chr->gcontext);
}
}

View File

@ -180,6 +180,17 @@ void qemu_chr_be_write(Chardev *s, uint8_t *buf, int len)
}
}
void qemu_chr_be_update_read_handlers(Chardev *s,
GMainContext *context)
{
ChardevClass *cc = CHARDEV_GET_CLASS(s);
s->gcontext = context;
if (cc->chr_update_read_handler) {
cc->chr_update_read_handler(s);
}
}
int qemu_chr_add_client(Chardev *s, int fd)
{
return CHARDEV_GET_CLASS(s)->chr_add_client ?

60
configure vendored
View File

@ -290,6 +290,7 @@ netmap="no"
sdl=""
sdlabi=""
virtfs=""
mpath=""
vnc="yes"
sparse="no"
vde=""
@ -936,6 +937,10 @@ for opt do
;;
--enable-virtfs) virtfs="yes"
;;
--disable-mpath) mpath="no"
;;
--enable-mpath) mpath="yes"
;;
--disable-vnc) vnc="no"
;;
--enable-vnc) vnc="yes"
@ -1479,6 +1484,7 @@ disabled with --disable-FEATURE, default is enabled if available:
vnc-png PNG compression for VNC server
cocoa Cocoa UI (Mac OS X only)
virtfs VirtFS
mpath Multipath persistent reservation passthrough
xen xen backend driver support
xen-pci-passthrough
brlapi BrlAPI (Braile)
@ -3294,6 +3300,30 @@ else
"Please install the pixman devel package."
fi
##########################################
# libmpathpersist probe
if test "$mpath" != "no" ; then
cat > $TMPC <<EOF
#include <libudev.h>
#include <mpath_persist.h>
unsigned mpath_mx_alloc_len = 1024;
int logsink;
int main(void) {
struct udev *udev = udev_new();
mpath_lib_init(udev);
return 0;
}
EOF
if compile_prog "" "-ludev -lmultipath -lmpathpersist" ; then
mpathpersist=yes
else
mpathpersist=no
fi
else
mpathpersist=no
fi
##########################################
# libcap probe
@ -5023,16 +5053,34 @@ if test "$want_tools" = "yes" ; then
fi
fi
if test "$softmmu" = yes ; then
if test "$virtfs" != no ; then
if test "$cap" = yes && test "$linux" = yes && test "$attr" = yes ; then
if test "$linux" = yes; then
if test "$virtfs" != no && test "$cap" = yes && test "$attr" = yes ; then
virtfs=yes
tools="$tools fsdev/virtfs-proxy-helper\$(EXESUF)"
else
if test "$virtfs" = yes; then
error_exit "VirtFS is supported only on Linux and requires libcap devel and libattr devel"
error_exit "VirtFS requires libcap devel and libattr devel"
fi
virtfs=no
fi
if test "$mpath" != no && test "$mpathpersist" = yes ; then
mpath=yes
else
if test "$mpath" = yes; then
error_exit "Multipath requires libmpathpersist devel"
fi
mpath=no
fi
tools="$tools scsi/qemu-pr-helper\$(EXESUF)"
else
if test "$virtfs" = yes; then
error_exit "VirtFS is supported only on Linux"
fi
virtfs=no
if test "$mpath" = yes; then
error_exit "Multipath is supported only on Linux"
fi
mpath=no
fi
fi
@ -5278,6 +5326,7 @@ echo "Audio drivers $audio_drv_list"
echo "Block whitelist (rw) $block_drv_rw_whitelist"
echo "Block whitelist (ro) $block_drv_ro_whitelist"
echo "VirtFS support $virtfs"
echo "Multipath support $mpath"
echo "VNC support $vnc"
if test "$vnc" = "yes" ; then
echo "VNC SASL support $vnc_sasl"
@ -5729,6 +5778,9 @@ fi
if test "$virtfs" = "yes" ; then
echo "CONFIG_VIRTFS=y" >> $config_host_mak
fi
if test "$mpath" = "yes" ; then
echo "CONFIG_MPATH=y" >> $config_host_mak
fi
if test "$vhost_scsi" = "yes" ; then
echo "CONFIG_VHOST_SCSI=y" >> $config_host_mak
fi
@ -6510,7 +6562,7 @@ 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 tests/qemu-iotests tests/vm"
DIRS="$DIRS docs docs/interop fsdev"
DIRS="$DIRS docs docs/interop fsdev scsi"
DIRS="$DIRS pc-bios/optionrom pc-bios/spapr-rtas pc-bios/s390-ccw"
DIRS="$DIRS roms/seabios roms/vgabios"
DIRS="$DIRS qapi-generated"

5
cpus.c
View File

@ -1764,8 +1764,9 @@ void qemu_init_vcpu(CPUState *cpu)
/* If the target cpu hasn't set up any address spaces itself,
* give it the default one.
*/
AddressSpace *as = address_space_init_shareable(cpu->memory,
"cpu-memory");
AddressSpace *as = g_new0(AddressSpace, 1);
address_space_init(as, cpu->memory, "cpu-memory");
cpu->num_ases = 1;
cpu_address_space_init(cpu, as, 0);
}

View File

@ -63,11 +63,23 @@ operations:
typeof(*ptr) atomic_fetch_sub(ptr, val)
typeof(*ptr) atomic_fetch_and(ptr, val)
typeof(*ptr) atomic_fetch_or(ptr, val)
typeof(*ptr) atomic_fetch_xor(ptr, val)
typeof(*ptr) atomic_fetch_inc_nonzero(ptr)
typeof(*ptr) atomic_xchg(ptr, val)
typeof(*ptr) atomic_cmpxchg(ptr, old, new)
all of which return the old value of *ptr. These operations are
polymorphic; they operate on any type that is as wide as an int.
polymorphic; they operate on any type that is as wide as a pointer.
Similar operations return the new value of *ptr:
typeof(*ptr) atomic_inc_fetch(ptr)
typeof(*ptr) atomic_dec_fetch(ptr)
typeof(*ptr) atomic_add_fetch(ptr, val)
typeof(*ptr) atomic_sub_fetch(ptr, val)
typeof(*ptr) atomic_and_fetch(ptr, val)
typeof(*ptr) atomic_or_fetch(ptr, val)
typeof(*ptr) atomic_xor_fetch(ptr, val)
Sequentially consistent loads and stores can be done using:

View File

@ -0,0 +1,83 @@
..
======================================
Persistent reservation helper protocol
======================================
QEMU's SCSI passthrough devices, ``scsi-block`` and ``scsi-generic``,
can delegate implementation of persistent reservations to an external
(and typically privileged) program. Persistent Reservations allow
restricting access to block devices to specific initiators in a shared
storage setup.
For a more detailed reference please refer the the SCSI Primary
Commands standard, specifically the section on Reservations and the
"PERSISTENT RESERVE IN" and "PERSISTENT RESERVE OUT" commands.
This document describes the socket protocol used between QEMU's
``pr-manager-helper`` object and the external program.
.. contents::
Connection and initialization
-----------------------------
All data transmitted on the socket is big-endian.
After connecting to the helper program's socket, the helper starts a simple
feature negotiation process by writing four bytes corresponding to
the features it exposes (``supported_features``). QEMU reads it,
then writes four bytes corresponding to the desired features of the
helper program (``requested_features``).
If a bit is 1 in ``requested_features`` and 0 in ``supported_features``,
the corresponding feature is not supported by the helper and the connection
is closed. On the other hand, it is acceptable for a bit to be 0 in
``requested_features`` and 1 in ``supported_features``; in this case,
the helper will not enable the feature.
Right now no feature is defined, so the two parties always write four
zero bytes.
Command format
--------------
It is invalid to send multiple commands concurrently on the same
socket. It is however possible to connect multiple sockets to the
helper and send multiple commands to the helper for one or more
file descriptors.
A command consists of a request and a response. A request consists
of a 16-byte SCSI CDB. A file descriptor must be passed to the helper
together with the SCSI CDB using ancillary data.
The CDB has the following limitations:
- the command (stored in the first byte) must be one of 0x5E
(PERSISTENT RESERVE IN) or 0x5F (PERSISTENT RESERVE OUT).
- the allocation length (stored in bytes 7-8 of the CDB for PERSISTENT
RESERVE IN) or parameter list length (stored in bytes 5-8 of the CDB
for PERSISTENT RESERVE OUT) is limited to 8 KiB.
For PERSISTENT RESERVE OUT, the parameter list is sent right after the
CDB. The length of the parameter list is taken from the CDB itself.
The helper's reply has the following structure:
- 4 bytes for the SCSI status
- 4 bytes for the payload size (nonzero only for PERSISTENT RESERVE IN
and only if the SCSI status is 0x00, i.e. GOOD)
- 96 bytes for the SCSI sense data
- if the size is nonzero, the payload follows
The sense data is always sent to keep the protocol simple, even though
it is only valid if the SCSI status is CHECK CONDITION (0x02).
The payload size is always less than or equal to the allocation length
specified in the CDB for the PERSISTENT RESERVE IN command.
If the protocol is violated, the helper closes the socket.

111
docs/pr-manager.rst Normal file
View File

@ -0,0 +1,111 @@
======================================
Persistent reservation managers
======================================
SCSI persistent Reservations allow restricting access to block devices
to specific initiators in a shared storage setup. When implementing
clustering of virtual machines, it is a common requirement for virtual
machines to send persistent reservation SCSI commands. However,
the operating system restricts sending these commands to unprivileged
programs because incorrect usage can disrupt regular operation of the
storage fabric.
For this reason, QEMU's SCSI passthrough devices, ``scsi-block``
and ``scsi-generic`` (both are only available on Linux) can delegate
implementation of persistent reservations to a separate object,
the "persistent reservation manager". Only PERSISTENT RESERVE OUT and
PERSISTENT RESERVE IN commands are passed to the persistent reservation
manager object; other commands are processed by QEMU as usual.
-----------------------------------------
Defining a persistent reservation manager
-----------------------------------------
A persistent reservation manager is an instance of a subclass of the
"pr-manager" QOM class.
Right now only one subclass is defined, ``pr-manager-helper``, which
forwards the commands to an external privileged helper program
over Unix sockets. The helper program only allows sending persistent
reservation commands to devices for which QEMU has a file descriptor,
so that QEMU will not be able to effect persistent reservations
unless it has access to both the socket and the device.
``pr-manager-helper`` has a single string property, ``path``, which
accepts the path to the helper program's Unix socket. For example,
the following command line defines a ``pr-manager-helper`` object and
attaches it to a SCSI passthrough device::
$ qemu-system-x86_64
-device virtio-scsi \
-object pr-manager-helper,id=helper0,path=/var/run/qemu-pr-helper.sock
-drive if=none,id=hd,driver=raw,file.filename=/dev/sdb,file.pr-manager=helper0
-device scsi-block,drive=hd
Alternatively, using ``-blockdev``::
$ qemu-system-x86_64
-device virtio-scsi \
-object pr-manager-helper,id=helper0,path=/var/run/qemu-pr-helper.sock
-blockdev node-name=hd,driver=raw,file.driver=host_device,file.filename=/dev/sdb,file.pr-manager=helper0
-device scsi-block,drive=hd
----------------------------------
Invoking :program:`qemu-pr-helper`
----------------------------------
QEMU provides an implementation of the persistent reservation helper,
called :program:`qemu-pr-helper`. The helper should be started as a
system service and supports the following option:
-d, --daemon run in the background
-q, --quiet decrease verbosity
-v, --verbose increase verbosity
-f, --pidfile=path PID file when running as a daemon
-k, --socket=path path to the socket
-T, --trace=trace-opts tracing options
By default, the socket and PID file are placed in the runtime state
directory, for example :file:`/var/run/qemu-pr-helper.sock` and
:file:`/var/run/qemu-pr-helper.pid`. The PID file is not created
unless :option:`-d` is passed too.
:program:`qemu-pr-helper` can also use the systemd socket activation
protocol. In this case, the systemd socket unit should specify a
Unix stream socket, like this::
[Socket]
ListenStream=/var/run/qemu-pr-helper.sock
After connecting to the socket, :program:`qemu-pr-helper`` can optionally drop
root privileges, except for those capabilities that are needed for
its operation. To do this, add the following options:
-u, --user=user user to drop privileges to
-g, --group=group group to drop privileges to
---------------------------------------------
Multipath devices and persistent reservations
---------------------------------------------
Proper support of persistent reservation for multipath devices requires
communication with the multipath daemon, so that the reservation is
registered and applied when a path is newly discovered or becomes online
again. :command:`qemu-pr-helper` can do this if the ``libmpathpersist``
library was available on the system at build time.
As of August 2017, a reservation key must be specified in ``multipath.conf``
for ``multipathd`` to check for persistent reservation for newly
discovered paths or reinstated paths. The attribute can be added
to the ``defaults`` section or the ``multipaths`` section; for example::
multipaths {
multipath {
wwid XXXXXXXXXXXXXXXX
alias yellow
reservation_key 0x123abc
}
}
Linking :program:`qemu-pr-helper` to ``libmpathpersist`` does not impede
its usage on regular SCSI devices.

330
exec.c
View File

@ -187,21 +187,18 @@ typedef struct PhysPageMap {
} PhysPageMap;
struct AddressSpaceDispatch {
struct rcu_head rcu;
MemoryRegionSection *mru_section;
/* This is a multi-level map on the physical address space.
* The bottom level has pointers to MemoryRegionSections.
*/
PhysPageEntry phys_map;
PhysPageMap map;
AddressSpace *as;
};
#define SUBPAGE_IDX(addr) ((addr) & ~TARGET_PAGE_MASK)
typedef struct subpage_t {
MemoryRegion iomem;
AddressSpace *as;
FlatView *fv;
hwaddr base;
uint16_t sub_section[];
} subpage_t;
@ -361,7 +358,7 @@ static void phys_page_compact(PhysPageEntry *lp, Node *nodes)
}
}
static void phys_page_compact_all(AddressSpaceDispatch *d, int nodes_nb)
void address_space_dispatch_compact(AddressSpaceDispatch *d)
{
if (d->phys_map.skip) {
phys_page_compact(&d->phys_map, d->map.nodes);
@ -471,12 +468,13 @@ address_space_translate_internal(AddressSpaceDispatch *d, hwaddr addr, hwaddr *x
}
/* Called from RCU critical section */
static MemoryRegionSection address_space_do_translate(AddressSpace *as,
hwaddr addr,
hwaddr *xlat,
hwaddr *plen,
bool is_write,
bool is_mmio)
static MemoryRegionSection flatview_do_translate(FlatView *fv,
hwaddr addr,
hwaddr *xlat,
hwaddr *plen,
bool is_write,
bool is_mmio,
AddressSpace **target_as)
{
IOMMUTLBEntry iotlb;
MemoryRegionSection *section;
@ -484,8 +482,9 @@ static MemoryRegionSection address_space_do_translate(AddressSpace *as,
IOMMUMemoryRegionClass *imrc;
for (;;) {
AddressSpaceDispatch *d = atomic_rcu_read(&as->dispatch);
section = address_space_translate_internal(d, addr, &addr, plen, is_mmio);
section = address_space_translate_internal(
flatview_to_dispatch(fv), addr, &addr,
plen, is_mmio);
iommu_mr = memory_region_get_iommu(section->mr);
if (!iommu_mr) {
@ -502,7 +501,8 @@ static MemoryRegionSection address_space_do_translate(AddressSpace *as,
goto translate_fail;
}
as = iotlb.target_as;
fv = address_space_to_flatview(iotlb.target_as);
*target_as = iotlb.target_as;
}
*xlat = addr;
@ -524,8 +524,8 @@ IOMMUTLBEntry address_space_get_iotlb_entry(AddressSpace *as, hwaddr addr,
plen = (hwaddr)-1;
/* This can never be MMIO. */
section = address_space_do_translate(as, addr, &xlat, &plen,
is_write, false);
section = flatview_do_translate(address_space_to_flatview(as), addr,
&xlat, &plen, is_write, false, &as);
/* Illegal translation */
if (section.mr == &io_mem_unassigned) {
@ -548,7 +548,7 @@ IOMMUTLBEntry address_space_get_iotlb_entry(AddressSpace *as, hwaddr addr,
plen -= 1;
return (IOMMUTLBEntry) {
.target_as = section.address_space,
.target_as = as,
.iova = addr & ~plen,
.translated_addr = xlat & ~plen,
.addr_mask = plen,
@ -561,15 +561,15 @@ iotlb_fail:
}
/* Called from RCU critical section */
MemoryRegion *address_space_translate(AddressSpace *as, hwaddr addr,
hwaddr *xlat, hwaddr *plen,
bool is_write)
MemoryRegion *flatview_translate(FlatView *fv, hwaddr addr, hwaddr *xlat,
hwaddr *plen, bool is_write)
{
MemoryRegion *mr;
MemoryRegionSection section;
AddressSpace *as = NULL;
/* This can be MMIO, so setup MMIO bit. */
section = address_space_do_translate(as, addr, xlat, plen, is_write, true);
section = flatview_do_translate(fv, addr, xlat, plen, is_write, true, &as);
mr = section.mr;
if (xen_enabled() && memory_access_is_direct(mr, is_write)) {
@ -1219,7 +1219,7 @@ hwaddr memory_region_section_get_iotlb(CPUState *cpu,
} else {
AddressSpaceDispatch *d;
d = atomic_rcu_read(&section->address_space->dispatch);
d = flatview_to_dispatch(section->fv);
iotlb = section - d->map.sections;
iotlb += xlat;
}
@ -1245,7 +1245,7 @@ hwaddr memory_region_section_get_iotlb(CPUState *cpu,
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 subpage_t *subpage_init(FlatView *fv, hwaddr base);
static void *(*phys_mem_alloc)(size_t size, uint64_t *align) =
qemu_anon_ram_alloc;
@ -1302,8 +1302,9 @@ static void phys_sections_free(PhysPageMap *map)
g_free(map->nodes);
}
static void register_subpage(AddressSpaceDispatch *d, MemoryRegionSection *section)
static void register_subpage(FlatView *fv, MemoryRegionSection *section)
{
AddressSpaceDispatch *d = flatview_to_dispatch(fv);
subpage_t *subpage;
hwaddr base = section->offset_within_address_space
& TARGET_PAGE_MASK;
@ -1317,8 +1318,8 @@ static void register_subpage(AddressSpaceDispatch *d, MemoryRegionSection *secti
assert(existing->mr->subpage || existing->mr == &io_mem_unassigned);
if (!(existing->mr->subpage)) {
subpage = subpage_init(d->as, base);
subsection.address_space = d->as;
subpage = subpage_init(fv, base);
subsection.fv = fv;
subsection.mr = &subpage->iomem;
phys_page_set(d, base >> TARGET_PAGE_BITS, 1,
phys_section_add(&d->map, &subsection));
@ -1332,9 +1333,10 @@ static void register_subpage(AddressSpaceDispatch *d, MemoryRegionSection *secti
}
static void register_multipage(AddressSpaceDispatch *d,
static void register_multipage(FlatView *fv,
MemoryRegionSection *section)
{
AddressSpaceDispatch *d = flatview_to_dispatch(fv);
hwaddr start_addr = section->offset_within_address_space;
uint16_t section_index = phys_section_add(&d->map, section);
uint64_t num_pages = int128_get64(int128_rshift(section->size,
@ -1344,10 +1346,8 @@ static void register_multipage(AddressSpaceDispatch *d,
phys_page_set(d, start_addr >> TARGET_PAGE_BITS, num_pages, section_index);
}
static void mem_add(MemoryListener *listener, MemoryRegionSection *section)
void flatview_add_to_dispatch(FlatView *fv, MemoryRegionSection *section)
{
AddressSpace *as = container_of(listener, AddressSpace, dispatch_listener);
AddressSpaceDispatch *d = as->next_dispatch;
MemoryRegionSection now = *section, remain = *section;
Int128 page_size = int128_make64(TARGET_PAGE_SIZE);
@ -1356,7 +1356,7 @@ static void mem_add(MemoryListener *listener, MemoryRegionSection *section)
- now.offset_within_address_space;
now.size = int128_min(int128_make64(left), now.size);
register_subpage(d, &now);
register_subpage(fv, &now);
} else {
now.size = int128_zero();
}
@ -1366,13 +1366,13 @@ static void mem_add(MemoryListener *listener, MemoryRegionSection *section)
remain.offset_within_region += int128_get64(now.size);
now = remain;
if (int128_lt(remain.size, page_size)) {
register_subpage(d, &now);
register_subpage(fv, &now);
} else if (remain.offset_within_address_space & ~TARGET_PAGE_MASK) {
now.size = page_size;
register_subpage(d, &now);
register_subpage(fv, &now);
} else {
now.size = int128_and(now.size, int128_neg(page_size));
register_multipage(d, &now);
register_multipage(fv, &now);
}
}
}
@ -2500,6 +2500,11 @@ static const MemoryRegionOps watch_mem_ops = {
.endianness = DEVICE_NATIVE_ENDIAN,
};
static MemTxResult flatview_write(FlatView *fv, hwaddr addr, MemTxAttrs attrs,
const uint8_t *buf, int len);
static bool flatview_access_valid(FlatView *fv, hwaddr addr, int len,
bool is_write);
static MemTxResult subpage_read(void *opaque, hwaddr addr, uint64_t *data,
unsigned len, MemTxAttrs attrs)
{
@ -2511,8 +2516,7 @@ static MemTxResult subpage_read(void *opaque, hwaddr addr, uint64_t *data,
printf("%s: subpage %p len %u addr " TARGET_FMT_plx "\n", __func__,
subpage, len, addr);
#endif
res = address_space_read(subpage->as, addr + subpage->base,
attrs, buf, len);
res = flatview_read(subpage->fv, addr + subpage->base, attrs, buf, len);
if (res) {
return res;
}
@ -2561,8 +2565,7 @@ static MemTxResult subpage_write(void *opaque, hwaddr addr,
default:
abort();
}
return address_space_write(subpage->as, addr + subpage->base,
attrs, buf, len);
return flatview_write(subpage->fv, addr + subpage->base, attrs, buf, len);
}
static bool subpage_accepts(void *opaque, hwaddr addr,
@ -2574,8 +2577,8 @@ static bool subpage_accepts(void *opaque, hwaddr addr,
__func__, subpage, is_write ? 'w' : 'r', len, addr);
#endif
return address_space_access_valid(subpage->as, addr + subpage->base,
len, is_write);
return flatview_access_valid(subpage->fv, addr + subpage->base,
len, is_write);
}
static const MemoryRegionOps subpage_ops = {
@ -2609,12 +2612,12 @@ static int subpage_register (subpage_t *mmio, uint32_t start, uint32_t end,
return 0;
}
static subpage_t *subpage_init(AddressSpace *as, hwaddr base)
static subpage_t *subpage_init(FlatView *fv, hwaddr base)
{
subpage_t *mmio;
mmio = g_malloc0(sizeof(subpage_t) + TARGET_PAGE_SIZE * sizeof(uint16_t));
mmio->as = as;
mmio->fv = fv;
mmio->base = base;
memory_region_init_io(&mmio->iomem, NULL, &subpage_ops, mmio,
NULL, TARGET_PAGE_SIZE);
@ -2628,12 +2631,11 @@ static subpage_t *subpage_init(AddressSpace *as, hwaddr base)
return mmio;
}
static uint16_t dummy_section(PhysPageMap *map, AddressSpace *as,
MemoryRegion *mr)
static uint16_t dummy_section(PhysPageMap *map, FlatView *fv, MemoryRegion *mr)
{
assert(as);
assert(fv);
MemoryRegionSection section = {
.address_space = as,
.fv = fv,
.mr = mr,
.offset_within_address_space = 0,
.offset_within_region = 0,
@ -2670,46 +2672,31 @@ static void io_mem_init(void)
NULL, UINT64_MAX);
}
static void mem_begin(MemoryListener *listener)
AddressSpaceDispatch *address_space_dispatch_new(FlatView *fv)
{
AddressSpace *as = container_of(listener, AddressSpace, dispatch_listener);
AddressSpaceDispatch *d = g_new0(AddressSpaceDispatch, 1);
uint16_t n;
n = dummy_section(&d->map, as, &io_mem_unassigned);
n = dummy_section(&d->map, fv, &io_mem_unassigned);
assert(n == PHYS_SECTION_UNASSIGNED);
n = dummy_section(&d->map, as, &io_mem_notdirty);
n = dummy_section(&d->map, fv, &io_mem_notdirty);
assert(n == PHYS_SECTION_NOTDIRTY);
n = dummy_section(&d->map, as, &io_mem_rom);
n = dummy_section(&d->map, fv, &io_mem_rom);
assert(n == PHYS_SECTION_ROM);
n = dummy_section(&d->map, as, &io_mem_watch);
n = dummy_section(&d->map, fv, &io_mem_watch);
assert(n == PHYS_SECTION_WATCH);
d->phys_map = (PhysPageEntry) { .ptr = PHYS_MAP_NODE_NIL, .skip = 1 };
d->as = as;
as->next_dispatch = d;
return d;
}
static void address_space_dispatch_free(AddressSpaceDispatch *d)
void address_space_dispatch_free(AddressSpaceDispatch *d)
{
phys_sections_free(&d->map);
g_free(d);
}
static void mem_commit(MemoryListener *listener)
{
AddressSpace *as = container_of(listener, AddressSpace, dispatch_listener);
AddressSpaceDispatch *cur = as->dispatch;
AddressSpaceDispatch *next = as->next_dispatch;
phys_page_compact_all(next, next->map.nodes_nb);
atomic_rcu_set(&as->dispatch, next);
if (cur) {
call_rcu(cur, address_space_dispatch_free, rcu);
}
}
static void tcg_commit(MemoryListener *listener)
{
CPUAddressSpace *cpuas;
@ -2723,39 +2710,11 @@ static void tcg_commit(MemoryListener *listener)
* We reload the dispatch pointer now because cpu_reloading_memory_map()
* may have split the RCU critical section.
*/
d = atomic_rcu_read(&cpuas->as->dispatch);
d = address_space_to_dispatch(cpuas->as);
atomic_rcu_set(&cpuas->memory_dispatch, d);
tlb_flush(cpuas->cpu);
}
void address_space_init_dispatch(AddressSpace *as)
{
as->dispatch = NULL;
as->dispatch_listener = (MemoryListener) {
.begin = mem_begin,
.commit = mem_commit,
.region_add = mem_add,
.region_nop = mem_add,
.priority = 0,
};
memory_listener_register(&as->dispatch_listener, as);
}
void address_space_unregister(AddressSpace *as)
{
memory_listener_unregister(&as->dispatch_listener);
}
void address_space_destroy_dispatch(AddressSpace *as)
{
AddressSpaceDispatch *d = as->dispatch;
atomic_rcu_set(&as->dispatch, NULL);
if (d) {
call_rcu(d, address_space_dispatch_free, rcu);
}
}
static void memory_map_init(void)
{
system_memory = g_malloc(sizeof(*system_memory));
@ -2899,11 +2858,11 @@ static bool prepare_mmio_access(MemoryRegion *mr)
}
/* Called within RCU critical section. */
static MemTxResult address_space_write_continue(AddressSpace *as, hwaddr addr,
MemTxAttrs attrs,
const uint8_t *buf,
int len, hwaddr addr1,
hwaddr l, MemoryRegion *mr)
static MemTxResult flatview_write_continue(FlatView *fv, hwaddr addr,
MemTxAttrs attrs,
const uint8_t *buf,
int len, hwaddr addr1,
hwaddr l, MemoryRegion *mr)
{
uint8_t *ptr;
uint64_t val;
@ -2965,14 +2924,14 @@ static MemTxResult address_space_write_continue(AddressSpace *as, hwaddr addr,
}
l = len;
mr = address_space_translate(as, addr, &addr1, &l, true);
mr = flatview_translate(fv, addr, &addr1, &l, true);
}
return result;
}
MemTxResult address_space_write(AddressSpace *as, hwaddr addr, MemTxAttrs attrs,
const uint8_t *buf, int len)
static MemTxResult flatview_write(FlatView *fv, hwaddr addr, MemTxAttrs attrs,
const uint8_t *buf, int len)
{
hwaddr l;
hwaddr addr1;
@ -2982,20 +2941,27 @@ MemTxResult address_space_write(AddressSpace *as, hwaddr addr, MemTxAttrs attrs,
if (len > 0) {
rcu_read_lock();
l = len;
mr = address_space_translate(as, addr, &addr1, &l, true);
result = address_space_write_continue(as, addr, attrs, buf, len,
addr1, l, mr);
mr = flatview_translate(fv, addr, &addr1, &l, true);
result = flatview_write_continue(fv, addr, attrs, buf, len,
addr1, l, mr);
rcu_read_unlock();
}
return result;
}
MemTxResult address_space_write(AddressSpace *as, hwaddr addr,
MemTxAttrs attrs,
const uint8_t *buf, int len)
{
return flatview_write(address_space_to_flatview(as), addr, attrs, buf, len);
}
/* Called within RCU critical section. */
MemTxResult address_space_read_continue(AddressSpace *as, hwaddr addr,
MemTxAttrs attrs, uint8_t *buf,
int len, hwaddr addr1, hwaddr l,
MemoryRegion *mr)
MemTxResult flatview_read_continue(FlatView *fv, hwaddr addr,
MemTxAttrs attrs, uint8_t *buf,
int len, hwaddr addr1, hwaddr l,
MemoryRegion *mr)
{
uint8_t *ptr;
uint64_t val;
@ -3055,14 +3021,14 @@ MemTxResult address_space_read_continue(AddressSpace *as, hwaddr addr,
}
l = len;
mr = address_space_translate(as, addr, &addr1, &l, false);
mr = flatview_translate(fv, addr, &addr1, &l, false);
}
return result;
}
MemTxResult address_space_read_full(AddressSpace *as, hwaddr addr,
MemTxAttrs attrs, uint8_t *buf, int len)
MemTxResult flatview_read_full(FlatView *fv, hwaddr addr,
MemTxAttrs attrs, uint8_t *buf, int len)
{
hwaddr l;
hwaddr addr1;
@ -3072,25 +3038,33 @@ MemTxResult address_space_read_full(AddressSpace *as, hwaddr addr,
if (len > 0) {
rcu_read_lock();
l = len;
mr = address_space_translate(as, addr, &addr1, &l, false);
result = address_space_read_continue(as, addr, attrs, buf, len,
addr1, l, mr);
mr = flatview_translate(fv, addr, &addr1, &l, false);
result = flatview_read_continue(fv, addr, attrs, buf, len,
addr1, l, mr);
rcu_read_unlock();
}
return result;
}
MemTxResult address_space_rw(AddressSpace *as, hwaddr addr, MemTxAttrs attrs,
uint8_t *buf, int len, bool is_write)
static MemTxResult flatview_rw(FlatView *fv, hwaddr addr, MemTxAttrs attrs,
uint8_t *buf, int len, bool is_write)
{
if (is_write) {
return address_space_write(as, addr, attrs, (uint8_t *)buf, len);
return flatview_write(fv, addr, attrs, (uint8_t *)buf, len);
} else {
return address_space_read(as, addr, attrs, (uint8_t *)buf, len);
return flatview_read(fv, addr, attrs, (uint8_t *)buf, len);
}
}
MemTxResult address_space_rw(AddressSpace *as, hwaddr addr,
MemTxAttrs attrs, uint8_t *buf,
int len, bool is_write)
{
return flatview_rw(address_space_to_flatview(as),
addr, attrs, buf, len, is_write);
}
void cpu_physical_memory_rw(hwaddr addr, uint8_t *buf,
int len, int is_write)
{
@ -3248,7 +3222,8 @@ static void cpu_notify_map_clients(void)
qemu_mutex_unlock(&map_client_list_lock);
}
bool address_space_access_valid(AddressSpace *as, hwaddr addr, int len, bool is_write)
static bool flatview_access_valid(FlatView *fv, hwaddr addr, int len,
bool is_write)
{
MemoryRegion *mr;
hwaddr l, xlat;
@ -3256,7 +3231,7 @@ bool address_space_access_valid(AddressSpace *as, hwaddr addr, int len, bool is_
rcu_read_lock();
while (len > 0) {
l = len;
mr = address_space_translate(as, addr, &xlat, &l, is_write);
mr = flatview_translate(fv, addr, &xlat, &l, is_write);
if (!memory_access_is_direct(mr, is_write)) {
l = memory_access_size(mr, l, addr);
if (!memory_region_access_valid(mr, xlat, l, is_write)) {
@ -3272,8 +3247,16 @@ bool address_space_access_valid(AddressSpace *as, hwaddr addr, int len, bool is_
return true;
}
bool address_space_access_valid(AddressSpace *as, hwaddr addr,
int len, bool is_write)
{
return flatview_access_valid(address_space_to_flatview(as),
addr, len, is_write);
}
static hwaddr
address_space_extend_translation(AddressSpace *as, hwaddr addr, hwaddr target_len,
flatview_extend_translation(FlatView *fv, hwaddr addr,
hwaddr target_len,
MemoryRegion *mr, hwaddr base, hwaddr len,
bool is_write)
{
@ -3290,7 +3273,8 @@ address_space_extend_translation(AddressSpace *as, hwaddr addr, hwaddr target_le
}
len = target_len;
this_mr = address_space_translate(as, addr, &xlat, &len, is_write);
this_mr = flatview_translate(fv, addr, &xlat,
&len, is_write);
if (this_mr != mr || xlat != base + done) {
return done;
}
@ -3313,6 +3297,7 @@ void *address_space_map(AddressSpace *as,
hwaddr l, xlat;
MemoryRegion *mr;
void *ptr;
FlatView *fv = address_space_to_flatview(as);
if (len == 0) {
return NULL;
@ -3320,7 +3305,7 @@ void *address_space_map(AddressSpace *as,
l = len;
rcu_read_lock();
mr = address_space_translate(as, addr, &xlat, &l, is_write);
mr = flatview_translate(fv, addr, &xlat, &l, is_write);
if (!memory_access_is_direct(mr, is_write)) {
if (atomic_xchg(&bounce.in_use, true)) {
@ -3336,7 +3321,7 @@ void *address_space_map(AddressSpace *as,
memory_region_ref(mr);
bounce.mr = mr;
if (!is_write) {
address_space_read(as, addr, MEMTXATTRS_UNSPECIFIED,
flatview_read(fv, addr, MEMTXATTRS_UNSPECIFIED,
bounce.buffer, l);
}
@ -3347,7 +3332,8 @@ void *address_space_map(AddressSpace *as,
memory_region_ref(mr);
*plen = address_space_extend_translation(as, addr, len, mr, xlat, l, is_write);
*plen = flatview_extend_translation(fv, addr, len, mr, xlat,
l, is_write);
ptr = qemu_ram_ptr_length(mr->ram_block, xlat, plen, true);
rcu_read_unlock();
@ -3630,3 +3616,87 @@ void page_size_init(void)
}
qemu_host_page_mask = -(intptr_t)qemu_host_page_size;
}
#if !defined(CONFIG_USER_ONLY)
static void mtree_print_phys_entries(fprintf_function mon, void *f,
int start, int end, int skip, int ptr)
{
if (start == end - 1) {
mon(f, "\t%3d ", start);
} else {
mon(f, "\t%3d..%-3d ", start, end - 1);
}
mon(f, " skip=%d ", skip);
if (ptr == PHYS_MAP_NODE_NIL) {
mon(f, " ptr=NIL");
} else if (!skip) {
mon(f, " ptr=#%d", ptr);
} else {
mon(f, " ptr=[%d]", ptr);
}
mon(f, "\n");
}
#define MR_SIZE(size) (int128_nz(size) ? (hwaddr)int128_get64( \
int128_sub((size), int128_one())) : 0)
void mtree_print_dispatch(fprintf_function mon, void *f,
AddressSpaceDispatch *d, MemoryRegion *root)
{
int i;
mon(f, " Dispatch\n");
mon(f, " Physical sections\n");
for (i = 0; i < d->map.sections_nb; ++i) {
MemoryRegionSection *s = d->map.sections + i;
const char *names[] = { " [unassigned]", " [not dirty]",
" [ROM]", " [watch]" };
mon(f, " #%d @" TARGET_FMT_plx ".." TARGET_FMT_plx " %s%s%s%s%s",
i,
s->offset_within_address_space,
s->offset_within_address_space + MR_SIZE(s->mr->size),
s->mr->name ? s->mr->name : "(noname)",
i < ARRAY_SIZE(names) ? names[i] : "",
s->mr == root ? " [ROOT]" : "",
s == d->mru_section ? " [MRU]" : "",
s->mr->is_iommu ? " [iommu]" : "");
if (s->mr->alias) {
mon(f, " alias=%s", s->mr->alias->name ?
s->mr->alias->name : "noname");
}
mon(f, "\n");
}
mon(f, " Nodes (%d bits per level, %d levels) ptr=[%d] skip=%d\n",
P_L2_BITS, P_L2_LEVELS, d->phys_map.ptr, d->phys_map.skip);
for (i = 0; i < d->map.nodes_nb; ++i) {
int j, jprev;
PhysPageEntry prev;
Node *n = d->map.nodes + i;
mon(f, " [%d]\n", i);
for (j = 0, jprev = 0, prev = *n[0]; j < ARRAY_SIZE(*n); ++j) {
PhysPageEntry *pe = *n + j;
if (pe->ptr == prev.ptr && pe->skip == prev.skip) {
continue;
}
mtree_print_phys_entries(mon, f, jprev, j, prev.skip, prev.ptr);
jprev = j;
prev = *pe;
}
if (jprev != ARRAY_SIZE(*n)) {
mtree_print_phys_entries(mon, f, jprev, j, prev.skip, prev.ptr);
}
}
}
#endif

View File

@ -250,9 +250,10 @@ ETEXI
{
.name = "mtree",
.args_type = "flatview:-f",
.params = "[-f]",
.help = "show memory tree (-f: dump flat view for address spaces)",
.args_type = "flatview:-f,dispatch_tree:-d",
.params = "[-f][-d]",
.help = "show memory tree (-f: dump flat view for address spaces;"
"-d: dump dispatch tree, valid with -f only)",
.cmd = hmp_info_mtree,
},

View File

@ -41,7 +41,7 @@ static MemTxResult bitband_read(void *opaque, hwaddr offset,
/* Find address in underlying memory and round down to multiple of size */
addr = bitband_addr(s, offset) & (-size);
res = address_space_read(s->source_as, addr, attrs, buf, size);
res = address_space_read(&s->source_as, addr, attrs, buf, size);
if (res) {
return res;
}
@ -66,7 +66,7 @@ static MemTxResult bitband_write(void *opaque, hwaddr offset, uint64_t value,
/* Find address in underlying memory and round down to multiple of size */
addr = bitband_addr(s, offset) & (-size);
res = address_space_read(s->source_as, addr, attrs, buf, size);
res = address_space_read(&s->source_as, addr, attrs, buf, size);
if (res) {
return res;
}
@ -79,7 +79,7 @@ static MemTxResult bitband_write(void *opaque, hwaddr offset, uint64_t value,
} else {
buf[bitpos >> 3] &= ~bit;
}
return address_space_write(s->source_as, addr, attrs, buf, size);
return address_space_write(&s->source_as, addr, attrs, buf, size);
}
static const MemoryRegionOps bitband_ops = {
@ -111,8 +111,7 @@ static void bitband_realize(DeviceState *dev, Error **errp)
return;
}
s->source_as = address_space_init_shareable(s->source_memory,
"bitband-source");
address_space_init(&s->source_as, s->source_memory, "bitband-source");
}
/* Board init. */

View File

@ -187,6 +187,26 @@ static int chr_be_change(void *opaque)
return 0;
}
static void virtconsole_enable_backend(VirtIOSerialPort *port, bool enable)
{
VirtConsole *vcon = VIRTIO_CONSOLE(port);
if (!qemu_chr_fe_backend_connected(&vcon->chr)) {
return;
}
if (enable) {
VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(port);
qemu_chr_fe_set_handlers(&vcon->chr, chr_can_read, chr_read,
k->is_console ? NULL : chr_event,
chr_be_change, vcon, NULL, false);
} else {
qemu_chr_fe_set_handlers(&vcon->chr, NULL, NULL, NULL,
NULL, NULL, NULL, false);
}
}
static void virtconsole_realize(DeviceState *dev, Error **errp)
{
VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(dev);
@ -258,6 +278,7 @@ static void virtserialport_class_init(ObjectClass *klass, void *data)
k->unrealize = virtconsole_unrealize;
k->have_data = flush_buf;
k->set_guest_connected = set_guest_connected;
k->enable_backend = virtconsole_enable_backend;
k->guest_writable = guest_writable;
dc->props = virtserialport_properties;
}

View File

@ -637,6 +637,13 @@ static void set_status(VirtIODevice *vdev, uint8_t status)
if (!(status & VIRTIO_CONFIG_S_DRIVER_OK)) {
guest_reset(vser);
}
QTAILQ_FOREACH(port, &vser->ports, next) {
VirtIOSerialPortClass *vsc = VIRTIO_SERIAL_PORT_GET_CLASS(port);
if (vsc->enable_backend) {
vsc->enable_backend(port, vdev->vm_running);
}
}
}
static void vser_reset(VirtIODevice *vdev)

View File

@ -124,7 +124,7 @@ static void kvm_openpic_region_add(MemoryListener *listener,
uint64_t reg_base;
int ret;
if (section->address_space != &address_space_memory) {
if (section->fv != address_space_to_flatview(&address_space_memory)) {
abort();
}

View File

@ -55,6 +55,7 @@ struct Chardev {
int logfd;
int be_open;
GSource *gsource;
GMainContext *gcontext;
DECLARE_BITMAP(features, QEMU_CHAR_FEATURE_LAST);
};
@ -168,6 +169,16 @@ void qemu_chr_be_write(Chardev *s, uint8_t *buf, int len);
*/
void qemu_chr_be_write_impl(Chardev *s, uint8_t *buf, int len);
/**
* @qemu_chr_be_update_read_handlers:
*
* Invoked when frontend read handlers are setup
*
* @context the gcontext that will be used to attach the watch sources
*/
void qemu_chr_be_update_read_handlers(Chardev *s,
GMainContext *context);
/**
* @qemu_chr_be_event:
*
@ -227,7 +238,7 @@ typedef struct ChardevClass {
int (*chr_write)(Chardev *s, const uint8_t *buf, int len);
int (*chr_sync_read)(Chardev *s, const uint8_t *buf, int len);
GSource *(*chr_add_watch)(Chardev *s, GIOCondition cond);
void (*chr_update_read_handler)(Chardev *s, GMainContext *context);
void (*chr_update_read_handler)(Chardev *s);
int (*chr_ioctl)(Chardev *s, int cmd, void *arg);
int (*get_msgfds)(Chardev *s, int* fds, int num);
int (*set_msgfds)(Chardev *s, int *fds, int num);

View File

@ -22,14 +22,22 @@
#ifndef CONFIG_USER_ONLY
typedef struct AddressSpaceDispatch AddressSpaceDispatch;
void address_space_init_dispatch(AddressSpace *as);
void address_space_unregister(AddressSpace *as);
void address_space_destroy_dispatch(AddressSpace *as);
extern const MemoryRegionOps unassigned_mem_ops;
bool memory_region_access_valid(MemoryRegion *mr, hwaddr addr,
unsigned size, bool is_write);
void flatview_add_to_dispatch(FlatView *fv, MemoryRegionSection *section);
AddressSpaceDispatch *address_space_dispatch_new(FlatView *fv);
void address_space_dispatch_compact(AddressSpaceDispatch *d);
AddressSpaceDispatch *address_space_to_dispatch(AddressSpace *as);
AddressSpaceDispatch *flatview_to_dispatch(FlatView *fv);
void address_space_dispatch_free(AddressSpaceDispatch *d);
void mtree_print_dispatch(fprintf_function mon, void *f,
struct AddressSpaceDispatch *d,
MemoryRegion *root);
#endif
#endif

View File

@ -308,21 +308,18 @@ struct AddressSpace {
struct rcu_head rcu;
char *name;
MemoryRegion *root;
int ref_count;
bool malloced;
/* Accessed via RCU. */
struct FlatView *current_map;
int ioeventfd_nb;
struct MemoryRegionIoeventfd *ioeventfds;
struct AddressSpaceDispatch *dispatch;
struct AddressSpaceDispatch *next_dispatch;
MemoryListener dispatch_listener;
QTAILQ_HEAD(memory_listeners_as, MemoryListener) listeners;
QTAILQ_ENTRY(AddressSpace) address_spaces_link;
};
FlatView *address_space_to_flatview(AddressSpace *as);
/**
* MemoryRegionSection: describes a fragment of a #MemoryRegion
*
@ -336,7 +333,7 @@ struct AddressSpace {
*/
struct MemoryRegionSection {
MemoryRegion *mr;
AddressSpace *address_space;
FlatView *fv;
hwaddr offset_within_region;
Int128 size;
hwaddr offset_within_address_space;
@ -1515,7 +1512,8 @@ void memory_global_dirty_log_start(void);
*/
void memory_global_dirty_log_stop(void);
void mtree_info(fprintf_function mon_printf, void *f, bool flatview);
void mtree_info(fprintf_function mon_printf, void *f, bool flatview,
bool dispatch_tree);
/**
* memory_region_request_mmio_ptr: request a pointer to an mmio
@ -1584,23 +1582,6 @@ MemTxResult memory_region_dispatch_write(MemoryRegion *mr,
*/
void address_space_init(AddressSpace *as, MemoryRegion *root, const char *name);
/**
* address_space_init_shareable: return an address space for a memory region,
* creating it if it does not already exist
*
* @root: a #MemoryRegion that routes addresses for the address space
* @name: an address space name. The name is only used for debugging
* output.
*
* This function will return a pointer to an existing AddressSpace
* which was initialized with the specified MemoryRegion, or it will
* create and initialize one if it does not already exist. The ASes
* are reference-counted, so the memory will be freed automatically
* when the AddressSpace is destroyed via address_space_destroy.
*/
AddressSpace *address_space_init_shareable(MemoryRegion *root,
const char *name);
/**
* address_space_destroy: destroy an address space
*
@ -1845,9 +1826,17 @@ IOMMUTLBEntry address_space_get_iotlb_entry(AddressSpace *as, hwaddr addr,
* @len: pointer to length
* @is_write: indicates the transfer direction
*/
MemoryRegion *address_space_translate(AddressSpace *as, hwaddr addr,
hwaddr *xlat, hwaddr *len,
bool is_write);
MemoryRegion *flatview_translate(FlatView *fv,
hwaddr addr, hwaddr *xlat,
hwaddr *len, bool is_write);
static inline MemoryRegion *address_space_translate(AddressSpace *as,
hwaddr addr, hwaddr *xlat,
hwaddr *len, bool is_write)
{
return flatview_translate(address_space_to_flatview(as),
addr, xlat, len, is_write);
}
/* address_space_access_valid: check for validity of accessing an address
* space range
@ -1898,12 +1887,13 @@ void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len,
/* Internal functions, part of the implementation of address_space_read. */
MemTxResult address_space_read_continue(AddressSpace *as, hwaddr addr,
MemTxAttrs attrs, uint8_t *buf,
int len, hwaddr addr1, hwaddr l,
MemoryRegion *mr);
MemTxResult address_space_read_full(AddressSpace *as, hwaddr addr,
MemTxAttrs attrs, uint8_t *buf, int len);
MemTxResult flatview_read_continue(FlatView *fv, hwaddr addr,
MemTxAttrs attrs, uint8_t *buf,
int len, hwaddr addr1, hwaddr l,
MemoryRegion *mr);
MemTxResult flatview_read_full(FlatView *fv, hwaddr addr,
MemTxAttrs attrs, uint8_t *buf, int len);
void *qemu_map_ram_ptr(RAMBlock *ram_block, ram_addr_t addr);
static inline bool memory_access_is_direct(MemoryRegion *mr, bool is_write)
@ -1930,8 +1920,8 @@ static inline bool memory_access_is_direct(MemoryRegion *mr, bool is_write)
* @buf: buffer with the data transferred
*/
static inline __attribute__((__always_inline__))
MemTxResult address_space_read(AddressSpace *as, hwaddr addr, MemTxAttrs attrs,
uint8_t *buf, int len)
MemTxResult flatview_read(FlatView *fv, hwaddr addr, MemTxAttrs attrs,
uint8_t *buf, int len)
{
MemTxResult result = MEMTX_OK;
hwaddr l, addr1;
@ -1942,22 +1932,29 @@ MemTxResult address_space_read(AddressSpace *as, hwaddr addr, MemTxAttrs attrs,
if (len) {
rcu_read_lock();
l = len;
mr = address_space_translate(as, addr, &addr1, &l, false);
mr = flatview_translate(fv, addr, &addr1, &l, false);
if (len == l && memory_access_is_direct(mr, false)) {
ptr = qemu_map_ram_ptr(mr->ram_block, addr1);
memcpy(buf, ptr, len);
} else {
result = address_space_read_continue(as, addr, attrs, buf, len,
addr1, l, mr);
result = flatview_read_continue(fv, addr, attrs, buf, len,
addr1, l, mr);
}
rcu_read_unlock();
}
} else {
result = address_space_read_full(as, addr, attrs, buf, len);
result = flatview_read_full(fv, addr, attrs, buf, len);
}
return result;
}
static inline MemTxResult address_space_read(AddressSpace *as, hwaddr addr,
MemTxAttrs attrs, uint8_t *buf,
int len)
{
return flatview_read(address_space_to_flatview(as), addr, attrs, buf, len);
}
/**
* address_space_read_cached: read from a cached RAM region
*

View File

@ -21,7 +21,7 @@ typedef struct {
SysBusDevice parent_obj;
/*< public >*/
AddressSpace *source_as;
AddressSpace source_as;
MemoryRegion iomem;
uint32_t base;
MemoryRegion *source_memory;

View File

@ -58,6 +58,9 @@ typedef struct VirtIOSerialPortClass {
/* Guest opened/closed device. */
void (*set_guest_connected)(VirtIOSerialPort *port, int guest_connected);
/* Enable/disable backend for virtio serial port */
void (*enable_backend)(VirtIOSerialPort *port, bool enable);
/* Guest is now ready to accept data (virtqueues set up). */
void (*guest_ready)(VirtIOSerialPort *port);

View File

@ -442,4 +442,12 @@
} while(0)
#endif
#define atomic_fetch_inc_nonzero(ptr) ({ \
typeof_strip_qual(*ptr) _oldn = atomic_read(ptr); \
while (_oldn && atomic_cmpxchg(ptr, _oldn, _oldn + 1) != _oldn) { \
_oldn = atomic_read(ptr); \
} \
_oldn; \
})
#endif /* QEMU_ATOMIC_H */

View File

@ -30,6 +30,7 @@ typedef struct DisplaySurface DisplaySurface;
typedef struct DriveInfo DriveInfo;
typedef struct Error Error;
typedef struct EventNotifier EventNotifier;
typedef struct FlatView FlatView;
typedef struct FWCfgEntry FWCfgEntry;
typedef struct FWCfgIoState FWCfgIoState;
typedef struct FWCfgMemState FWCfgMemState;

56
include/scsi/pr-manager.h Normal file
View File

@ -0,0 +1,56 @@
#ifndef PR_MANAGER_H
#define PR_MANAGER_H
#include "qom/object.h"
#include "qapi/qmp/qdict.h"
#include "qapi/visitor.h"
#include "qom/object_interfaces.h"
#include "block/aio.h"
#define TYPE_PR_MANAGER "pr-manager"
#define PR_MANAGER_CLASS(klass) \
OBJECT_CLASS_CHECK(PRManagerClass, (klass), TYPE_PR_MANAGER)
#define PR_MANAGER_GET_CLASS(obj) \
OBJECT_GET_CLASS(PRManagerClass, (obj), TYPE_PR_MANAGER)
#define PR_MANAGER(obj) \
OBJECT_CHECK(PRManager, (obj), TYPE_PR_MANAGER)
struct sg_io_hdr;
typedef struct PRManager {
/* <private> */
Object parent;
} PRManager;
/**
* PRManagerClass:
* @parent_class: the base class
* @run: callback invoked in thread pool context
*/
typedef struct PRManagerClass {
/* <private> */
ObjectClass parent_class;
/* <public> */
int (*run)(PRManager *pr_mgr, int fd, struct sg_io_hdr *hdr);
} PRManagerClass;
BlockAIOCB *pr_manager_execute(PRManager *pr_mgr,
AioContext *ctx, int fd,
struct sg_io_hdr *hdr,
BlockCompletionFunc *complete,
void *opaque);
#ifdef CONFIG_LINUX
PRManager *pr_manager_lookup(const char *id, Error **errp);
#else
static inline PRManager *pr_manager_lookup(const char *id, Error **errp)
{
/* The classes do not exist at all! */
error_setg(errp, "No persistent reservation manager with id '%s'", id);
return NULL;
}
#endif
#endif

View File

@ -72,10 +72,14 @@ extern const struct SCSISense sense_code_IO_ERROR;
extern const struct SCSISense sense_code_I_T_NEXUS_LOSS;
/* Command aborted, Logical Unit failure */
extern const struct SCSISense sense_code_LUN_FAILURE;
/* Command aborted, LUN Communication failure */
extern const struct SCSISense sense_code_LUN_COMM_FAILURE;
/* Command aborted, Overlapped Commands Attempted */
extern const struct SCSISense sense_code_OVERLAPPED_COMMANDS;
/* LUN not ready, Capacity data has changed */
extern const struct SCSISense sense_code_CAPACITY_CHANGED;
/* Unit attention, SCSI bus reset */
extern const struct SCSISense sense_code_SCSI_BUS_RESET;
/* LUN not ready, Medium not present */
extern const struct SCSISense sense_code_UNIT_ATTENTION_NO_MEDIUM;
/* Unit attention, Power on, reset or bus device reset occurred */

382
memory.c
View File

@ -47,6 +47,8 @@ static QTAILQ_HEAD(memory_listeners, MemoryListener) memory_listeners
static QTAILQ_HEAD(, AddressSpace) address_spaces
= QTAILQ_HEAD_INITIALIZER(address_spaces);
static GHashTable *flat_views;
typedef struct AddrRange AddrRange;
/*
@ -154,7 +156,8 @@ enum ListenerDirection { Forward, Reverse };
/* No need to ref/unref .mr, the FlatRange keeps it alive. */
#define MEMORY_LISTENER_UPDATE_REGION(fr, as, dir, callback, _args...) \
do { \
MemoryRegionSection mrs = section_from_flat_range(fr, as); \
MemoryRegionSection mrs = section_from_flat_range(fr, \
address_space_to_flatview(as)); \
MEMORY_LISTENER_CALL(as, callback, dir, &mrs, ##_args); \
} while(0)
@ -208,7 +211,6 @@ static bool memory_region_ioeventfd_equal(MemoryRegionIoeventfd a,
}
typedef struct FlatRange FlatRange;
typedef struct FlatView FlatView;
/* Range of memory in the global map. Addresses are absolute. */
struct FlatRange {
@ -229,6 +231,8 @@ struct FlatView {
FlatRange *ranges;
unsigned nr;
unsigned nr_allocated;
struct AddressSpaceDispatch *dispatch;
MemoryRegion *root;
};
typedef struct AddressSpaceOps AddressSpaceOps;
@ -237,11 +241,11 @@ typedef struct AddressSpaceOps AddressSpaceOps;
for (var = (view)->ranges; var < (view)->ranges + (view)->nr; ++var)
static inline MemoryRegionSection
section_from_flat_range(FlatRange *fr, AddressSpace *as)
section_from_flat_range(FlatRange *fr, FlatView *fv)
{
return (MemoryRegionSection) {
.mr = fr->mr,
.address_space = as,
.fv = fv,
.offset_within_region = fr->offset_in_region,
.size = fr->addr.size,
.offset_within_address_space = int128_get64(fr->addr.start),
@ -258,12 +262,17 @@ static bool flatrange_equal(FlatRange *a, FlatRange *b)
&& a->readonly == b->readonly;
}
static void flatview_init(FlatView *view)
static FlatView *flatview_new(MemoryRegion *mr_root)
{
FlatView *view;
view = g_new0(FlatView, 1);
view->ref = 1;
view->ranges = NULL;
view->nr = 0;
view->nr_allocated = 0;
view->root = mr_root;
memory_region_ref(mr_root);
trace_flatview_new(view, mr_root);
return view;
}
/* Insert a range into a given position. Caller is responsible for maintaining
@ -287,25 +296,47 @@ static void flatview_destroy(FlatView *view)
{
int i;
trace_flatview_destroy(view, view->root);
if (view->dispatch) {
address_space_dispatch_free(view->dispatch);
}
for (i = 0; i < view->nr; i++) {
memory_region_unref(view->ranges[i].mr);
}
g_free(view->ranges);
memory_region_unref(view->root);
g_free(view);
}
static void flatview_ref(FlatView *view)
static bool flatview_ref(FlatView *view)
{
atomic_inc(&view->ref);
return atomic_fetch_inc_nonzero(&view->ref) > 0;
}
static void flatview_unref(FlatView *view)
{
if (atomic_fetch_dec(&view->ref) == 1) {
flatview_destroy(view);
trace_flatview_destroy_rcu(view, view->root);
assert(view->root);
call_rcu(view, flatview_destroy, rcu);
}
}
FlatView *address_space_to_flatview(AddressSpace *as)
{
return atomic_rcu_read(&as->current_map);
}
AddressSpaceDispatch *flatview_to_dispatch(FlatView *fv)
{
return fv->dispatch;
}
AddressSpaceDispatch *address_space_to_dispatch(AddressSpace *as)
{
return flatview_to_dispatch(address_space_to_flatview(as));
}
static bool can_merge(FlatRange *r1, FlatRange *r2)
{
return int128_eq(addrrange_end(r1->addr), r2->addr.start)
@ -560,13 +591,14 @@ static MemTxResult access_with_adjusted_size(hwaddr addr,
unsigned size,
unsigned access_size_min,
unsigned access_size_max,
MemTxResult (*access)(MemoryRegion *mr,
hwaddr addr,
uint64_t *value,
unsigned size,
unsigned shift,
uint64_t mask,
MemTxAttrs attrs),
MemTxResult (*access_fn)
(MemoryRegion *mr,
hwaddr addr,
uint64_t *value,
unsigned size,
unsigned shift,
uint64_t mask,
MemTxAttrs attrs),
MemoryRegion *mr,
MemTxAttrs attrs)
{
@ -587,12 +619,12 @@ static MemTxResult access_with_adjusted_size(hwaddr addr,
access_mask = -1ULL >> (64 - access_size * 8);
if (memory_region_big_endian(mr)) {
for (i = 0; i < size; i += access_size) {
r |= access(mr, addr + i, value, access_size,
r |= access_fn(mr, addr + i, value, access_size,
(size - access_size - i) * 8, access_mask, attrs);
}
} else {
for (i = 0; i < size; i += access_size) {
r |= access(mr, addr + i, value, access_size, i * 8,
r |= access_fn(mr, addr + i, value, access_size, i * 8,
access_mask, attrs);
}
}
@ -701,13 +733,57 @@ static void render_memory_region(FlatView *view,
}
}
static MemoryRegion *memory_region_get_flatview_root(MemoryRegion *mr)
{
while (mr->enabled) {
if (mr->alias) {
if (!mr->alias_offset && int128_ge(mr->size, mr->alias->size)) {
/* The alias is included in its entirety. Use it as
* the "real" root, so that we can share more FlatViews.
*/
mr = mr->alias;
continue;
}
} else if (!mr->terminates) {
unsigned int found = 0;
MemoryRegion *child, *next = NULL;
QTAILQ_FOREACH(child, &mr->subregions, subregions_link) {
if (child->enabled) {
if (++found > 1) {
next = NULL;
break;
}
if (!child->addr && int128_ge(mr->size, child->size)) {
/* A child is included in its entirety. If it's the only
* enabled one, use it in the hope of finding an alias down the
* way. This will also let us share FlatViews.
*/
next = child;
}
}
}
if (found == 0) {
return NULL;
}
if (next) {
mr = next;
continue;
}
}
return mr;
}
return NULL;
}
/* Render a memory topology into a list of disjoint absolute ranges. */
static FlatView *generate_memory_topology(MemoryRegion *mr)
{
int i;
FlatView *view;
view = g_new(FlatView, 1);
flatview_init(view);
view = flatview_new(mr);
if (mr) {
render_memory_region(view, mr, int128_zero(),
@ -715,6 +791,15 @@ static FlatView *generate_memory_topology(MemoryRegion *mr)
}
flatview_simplify(view);
view->dispatch = address_space_dispatch_new(view);
for (i = 0; i < view->nr; i++) {
MemoryRegionSection mrs =
section_from_flat_range(&view->ranges[i], view);
flatview_add_to_dispatch(view, &mrs);
}
address_space_dispatch_compact(view->dispatch);
g_hash_table_replace(flat_views, mr, view);
return view;
}
@ -740,7 +825,7 @@ static void address_space_add_del_ioeventfds(AddressSpace *as,
fds_new[inew]))) {
fd = &fds_old[iold];
section = (MemoryRegionSection) {
.address_space = as,
.fv = address_space_to_flatview(as),
.offset_within_address_space = int128_get64(fd->addr.start),
.size = fd->addr.size,
};
@ -753,7 +838,7 @@ static void address_space_add_del_ioeventfds(AddressSpace *as,
fds_old[iold]))) {
fd = &fds_new[inew];
section = (MemoryRegionSection) {
.address_space = as,
.fv = address_space_to_flatview(as),
.offset_within_address_space = int128_get64(fd->addr.start),
.size = fd->addr.size,
};
@ -772,8 +857,12 @@ static FlatView *address_space_get_flatview(AddressSpace *as)
FlatView *view;
rcu_read_lock();
view = atomic_rcu_read(&as->current_map);
flatview_ref(view);
do {
view = address_space_to_flatview(as);
/* If somebody has replaced as->current_map concurrently,
* flatview_ref returns false.
*/
} while (!flatview_ref(view));
rcu_read_unlock();
return view;
}
@ -879,18 +968,81 @@ static void address_space_update_topology_pass(AddressSpace *as,
}
}
static void address_space_update_topology(AddressSpace *as)
static void flatviews_init(void)
{
FlatView *old_view = address_space_get_flatview(as);
FlatView *new_view = generate_memory_topology(as->root);
static FlatView *empty_view;
address_space_update_topology_pass(as, old_view, new_view, false);
address_space_update_topology_pass(as, old_view, new_view, true);
if (flat_views) {
return;
}
flat_views = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL,
(GDestroyNotify) flatview_unref);
if (!empty_view) {
empty_view = generate_memory_topology(NULL);
/* We keep it alive forever in the global variable. */
flatview_ref(empty_view);
} else {
g_hash_table_replace(flat_views, NULL, empty_view);
flatview_ref(empty_view);
}
}
static void flatviews_reset(void)
{
AddressSpace *as;
if (flat_views) {
g_hash_table_unref(flat_views);
flat_views = NULL;
}
flatviews_init();
/* Render unique FVs */
QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
MemoryRegion *physmr = memory_region_get_flatview_root(as->root);
if (g_hash_table_lookup(flat_views, physmr)) {
continue;
}
generate_memory_topology(physmr);
}
}
static void address_space_set_flatview(AddressSpace *as)
{
FlatView *old_view = address_space_to_flatview(as);
MemoryRegion *physmr = memory_region_get_flatview_root(as->root);
FlatView *new_view = g_hash_table_lookup(flat_views, physmr);
assert(new_view);
if (old_view == new_view) {
return;
}
if (old_view) {
flatview_ref(old_view);
}
flatview_ref(new_view);
if (!QTAILQ_EMPTY(&as->listeners)) {
FlatView tmpview = { .nr = 0 }, *old_view2 = old_view;
if (!old_view2) {
old_view2 = &tmpview;
}
address_space_update_topology_pass(as, old_view2, new_view, false);
address_space_update_topology_pass(as, old_view2, new_view, true);
}
/* Writes are protected by the BQL. */
atomic_rcu_set(&as->current_map, new_view);
call_rcu(old_view, flatview_unref, rcu);
if (old_view) {
flatview_unref(old_view);
}
/* Note that all the old MemoryRegions are still alive up to this
* point. This relieves most MemoryListeners from the need to
@ -898,9 +1050,20 @@ static void address_space_update_topology(AddressSpace *as)
* outside the iothread mutex, in which case precise reference
* counting is necessary.
*/
flatview_unref(old_view);
if (old_view) {
flatview_unref(old_view);
}
}
address_space_update_ioeventfds(as);
static void address_space_update_topology(AddressSpace *as)
{
MemoryRegion *physmr = memory_region_get_flatview_root(as->root);
flatviews_init();
if (!g_hash_table_lookup(flat_views, physmr)) {
generate_memory_topology(physmr);
}
address_space_set_flatview(as);
}
void memory_region_transaction_begin(void)
@ -919,10 +1082,13 @@ void memory_region_transaction_commit(void)
--memory_region_transaction_depth;
if (!memory_region_transaction_depth) {
if (memory_region_update_pending) {
flatviews_reset();
MEMORY_LISTENER_CALL_GLOBAL(begin, Forward);
QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
address_space_update_topology(as);
address_space_set_flatview(as);
address_space_update_ioeventfds(as);
}
memory_region_update_pending = false;
MEMORY_LISTENER_CALL_GLOBAL(commit, Forward);
@ -1835,7 +2001,7 @@ void memory_region_sync_dirty_bitmap(MemoryRegion *mr)
view = address_space_get_flatview(as);
FOR_EACH_FLAT_RANGE(fr, view) {
if (fr->mr == mr) {
MemoryRegionSection mrs = section_from_flat_range(fr, as);
MemoryRegionSection mrs = section_from_flat_range(fr, view);
listener->log_sync(listener, &mrs);
}
}
@ -1938,7 +2104,7 @@ static void memory_region_update_coalesced_range_as(MemoryRegion *mr, AddressSpa
FOR_EACH_FLAT_RANGE(fr, view) {
if (fr->mr == mr) {
section = (MemoryRegionSection) {
.address_space = as,
.fv = view,
.offset_within_address_space = int128_get64(fr->addr.start),
.size = fr->addr.size,
};
@ -2289,7 +2455,7 @@ static MemoryRegionSection memory_region_find_rcu(MemoryRegion *mr,
}
range = addrrange_make(int128_make64(addr), int128_make64(size));
view = atomic_rcu_read(&as->current_map);
view = address_space_to_flatview(as);
fr = flatview_lookup(view, range);
if (!fr) {
return ret;
@ -2300,7 +2466,7 @@ static MemoryRegionSection memory_region_find_rcu(MemoryRegion *mr,
}
ret.mr = fr->mr;
ret.address_space = as;
ret.fv = view;
range = addrrange_intersection(range, fr->addr);
ret.offset_within_region = fr->offset_in_region;
ret.offset_within_region += int128_get64(int128_sub(range.start,
@ -2349,7 +2515,8 @@ void memory_global_dirty_log_sync(void)
view = address_space_get_flatview(as);
FOR_EACH_FLAT_RANGE(fr, view) {
if (fr->dirty_log_mask) {
MemoryRegionSection mrs = section_from_flat_range(fr, as);
MemoryRegionSection mrs = section_from_flat_range(fr, view);
listener->log_sync(listener, &mrs);
}
}
@ -2434,7 +2601,7 @@ static void listener_add_address_space(MemoryListener *listener,
FOR_EACH_FLAT_RANGE(fr, view) {
MemoryRegionSection section = {
.mr = fr->mr,
.address_space = as,
.fv = view,
.offset_within_region = fr->offset_in_region,
.size = fr->addr.size,
.offset_within_address_space = int128_get64(fr->addr.start),
@ -2610,69 +2777,36 @@ void memory_region_invalidate_mmio_ptr(MemoryRegion *mr, hwaddr offset,
void address_space_init(AddressSpace *as, MemoryRegion *root, const char *name)
{
memory_region_ref(root);
memory_region_transaction_begin();
as->ref_count = 1;
as->root = root;
as->malloced = false;
as->current_map = g_new(FlatView, 1);
flatview_init(as->current_map);
as->current_map = NULL;
as->ioeventfd_nb = 0;
as->ioeventfds = NULL;
QTAILQ_INIT(&as->listeners);
QTAILQ_INSERT_TAIL(&address_spaces, as, address_spaces_link);
as->name = g_strdup(name ? name : "anonymous");
address_space_init_dispatch(as);
memory_region_update_pending |= root->enabled;
memory_region_transaction_commit();
address_space_update_topology(as);
address_space_update_ioeventfds(as);
}
static void do_address_space_destroy(AddressSpace *as)
{
bool do_free = as->malloced;
address_space_destroy_dispatch(as);
assert(QTAILQ_EMPTY(&as->listeners));
flatview_unref(as->current_map);
g_free(as->name);
g_free(as->ioeventfds);
memory_region_unref(as->root);
if (do_free) {
g_free(as);
}
}
AddressSpace *address_space_init_shareable(MemoryRegion *root, const char *name)
{
AddressSpace *as;
QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
if (root == as->root && as->malloced) {
as->ref_count++;
return as;
}
}
as = g_malloc0(sizeof *as);
address_space_init(as, root, name);
as->malloced = true;
return as;
}
void address_space_destroy(AddressSpace *as)
{
MemoryRegion *root = as->root;
as->ref_count--;
if (as->ref_count) {
return;
}
/* Flush out anything from MemoryListeners listening in on this */
memory_region_transaction_begin();
as->root = NULL;
memory_region_transaction_commit();
QTAILQ_REMOVE(&address_spaces, as, address_spaces_link);
address_space_unregister(as);
/* At this point, as->dispatch and as->current_map are dummy
* entries that the guest should never use. Wait for the old
@ -2807,18 +2941,44 @@ static void mtree_print_mr(fprintf_function mon_printf, void *f,
}
}
static void mtree_print_flatview(fprintf_function p, void *f,
AddressSpace *as)
struct FlatViewInfo {
fprintf_function mon_printf;
void *f;
int counter;
bool dispatch_tree;
};
static void mtree_print_flatview(gpointer key, gpointer value,
gpointer user_data)
{
FlatView *view = address_space_get_flatview(as);
FlatView *view = key;
GArray *fv_address_spaces = value;
struct FlatViewInfo *fvi = user_data;
fprintf_function p = fvi->mon_printf;
void *f = fvi->f;
FlatRange *range = &view->ranges[0];
MemoryRegion *mr;
int n = view->nr;
int i;
AddressSpace *as;
p(f, "FlatView #%d\n", fvi->counter);
++fvi->counter;
for (i = 0; i < fv_address_spaces->len; ++i) {
as = g_array_index(fv_address_spaces, AddressSpace*, i);
p(f, " AS \"%s\", root: %s", as->name, memory_region_name(as->root));
if (as->root->alias) {
p(f, ", alias %s", memory_region_name(as->root->alias));
}
p(f, "\n");
}
p(f, " Root memory region: %s\n",
view->root ? memory_region_name(view->root) : "(none)");
if (n <= 0) {
p(f, MTREE_INDENT "No rendered FlatView for "
"address space '%s'\n", as->name);
flatview_unref(view);
p(f, MTREE_INDENT "No rendered FlatView\n\n");
return;
}
@ -2845,21 +3005,65 @@ static void mtree_print_flatview(fprintf_function p, void *f,
range++;
}
flatview_unref(view);
#if !defined(CONFIG_USER_ONLY)
if (fvi->dispatch_tree && view->root) {
mtree_print_dispatch(p, f, view->dispatch, view->root);
}
#endif
p(f, "\n");
}
void mtree_info(fprintf_function mon_printf, void *f, bool flatview)
static gboolean mtree_info_flatview_free(gpointer key, gpointer value,
gpointer user_data)
{
FlatView *view = key;
GArray *fv_address_spaces = value;
g_array_unref(fv_address_spaces);
flatview_unref(view);
return true;
}
void mtree_info(fprintf_function mon_printf, void *f, bool flatview,
bool dispatch_tree)
{
MemoryRegionListHead ml_head;
MemoryRegionList *ml, *ml2;
AddressSpace *as;
if (flatview) {
FlatView *view;
struct FlatViewInfo fvi = {
.mon_printf = mon_printf,
.f = f,
.counter = 0,
.dispatch_tree = dispatch_tree
};
GArray *fv_address_spaces;
GHashTable *views = g_hash_table_new(g_direct_hash, g_direct_equal);
/* Gather all FVs in one table */
QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
mon_printf(f, "address-space (flat view): %s\n", as->name);
mtree_print_flatview(mon_printf, f, as);
mon_printf(f, "\n");
view = address_space_get_flatview(as);
fv_address_spaces = g_hash_table_lookup(views, view);
if (!fv_address_spaces) {
fv_address_spaces = g_array_new(false, false, sizeof(as));
g_hash_table_insert(views, view, fv_address_spaces);
}
g_array_append_val(fv_address_spaces, as);
}
/* Print */
g_hash_table_foreach(views, mtree_print_flatview, &fvi);
/* Free */
g_hash_table_foreach_remove(views, mtree_info_flatview_free, 0);
g_hash_table_unref(views);
return;
}

View File

@ -1703,8 +1703,9 @@ static void hmp_boot_set(Monitor *mon, const QDict *qdict)
static void hmp_info_mtree(Monitor *mon, const QDict *qdict)
{
bool flatview = qdict_get_try_bool(qdict, "flatview", false);
bool dispatch_tree = qdict_get_try_bool(qdict, "dispatch_tree", false);
mtree_info((fprintf_function)monitor_printf, mon, flatview);
mtree_info((fprintf_function)monitor_printf, mon, flatview, dispatch_tree);
}
static void hmp_info_numa(Monitor *mon, const QDict *qdict)

View File

@ -2241,6 +2241,9 @@
# Driver specific block device options for the file backend.
#
# @filename: path to the image file
# @pr-manager: the id for the object that will handle persistent reservations
# for this device (default: none, forward the commands via SG_IO;
# since 2.11)
# @aio: AIO backend (default: threads) (since: 2.8)
# @locking: whether to enable file locking. If set to 'auto', only enable
# when Open File Descriptor (OFD) locking API is available
@ -2250,6 +2253,7 @@
##
{ 'struct': 'BlockdevOptionsFile',
'data': { 'filename': 'str',
'*pr-manager': 'str',
'*locking': 'OnOffAuto',
'*aio': 'BlockdevAioOptions' } }

View File

@ -1 +1,3 @@
block-obj-y += utils.o
block-obj-$(CONFIG_LINUX) += pr-manager.o pr-manager-helper.o

41
scsi/pr-helper.h Normal file
View File

@ -0,0 +1,41 @@
/* Definitions for QEMU's persistent reservation helper daemon
*
* Copyright (C) 2017 Red Hat, Inc.
*
* Author:
* Paolo Bonzini <pbonzini@redhat.com>
*
* 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.
*/
#ifndef QEMU_PR_HELPER_H
#define QEMU_PR_HELPER_H 1
#include <stdint.h>
#define PR_HELPER_CDB_SIZE 16
#define PR_HELPER_SENSE_SIZE 96
#define PR_HELPER_DATA_SIZE 8192
typedef struct PRHelperResponse {
int32_t result;
int32_t sz;
uint8_t sense[PR_HELPER_SENSE_SIZE];
} PRHelperResponse;
#endif

302
scsi/pr-manager-helper.c Normal file
View File

@ -0,0 +1,302 @@
/*
* Persistent reservation manager that talks to qemu-pr-helper
*
* Copyright (c) 2017 Red Hat, Inc.
*
* Author: Paolo Bonzini <pbonzini@redhat.com>
*
* This code is licensed under the LGPL v2.1 or later.
*
*/
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "scsi/constants.h"
#include "scsi/pr-manager.h"
#include "scsi/utils.h"
#include "io/channel.h"
#include "io/channel-socket.h"
#include "pr-helper.h"
#include <scsi/sg.h>
#define PR_MAX_RECONNECT_ATTEMPTS 5
#define TYPE_PR_MANAGER_HELPER "pr-manager-helper"
#define PR_MANAGER_HELPER(obj) \
OBJECT_CHECK(PRManagerHelper, (obj), \
TYPE_PR_MANAGER_HELPER)
typedef struct PRManagerHelper {
/* <private> */
PRManager parent;
char *path;
QemuMutex lock;
QIOChannel *ioc;
} PRManagerHelper;
/* Called with lock held. */
static int pr_manager_helper_read(PRManagerHelper *pr_mgr,
void *buf, int sz, Error **errp)
{
ssize_t r = qio_channel_read_all(pr_mgr->ioc, buf, sz, errp);
if (r < 0) {
object_unref(OBJECT(pr_mgr->ioc));
pr_mgr->ioc = NULL;
return -EINVAL;
}
return 0;
}
/* Called with lock held. */
static int pr_manager_helper_write(PRManagerHelper *pr_mgr,
int fd,
const void *buf, int sz, Error **errp)
{
size_t nfds = (fd != -1);
while (sz > 0) {
struct iovec iov;
ssize_t n_written;
iov.iov_base = (void *)buf;
iov.iov_len = sz;
n_written = qio_channel_writev_full(QIO_CHANNEL(pr_mgr->ioc), &iov, 1,
nfds ? &fd : NULL, nfds, errp);
if (n_written <= 0) {
assert(n_written != QIO_CHANNEL_ERR_BLOCK);
object_unref(OBJECT(pr_mgr->ioc));
return n_written < 0 ? -EINVAL : 0;
}
nfds = 0;
buf += n_written;
sz -= n_written;
}
return 0;
}
/* Called with lock held. */
static int pr_manager_helper_initialize(PRManagerHelper *pr_mgr,
Error **errp)
{
char *path = g_strdup(pr_mgr->path);
SocketAddress saddr = {
.type = SOCKET_ADDRESS_TYPE_UNIX,
.u.q_unix.path = path
};
QIOChannelSocket *sioc = qio_channel_socket_new();
Error *local_err = NULL;
uint32_t flags;
int r;
assert(!pr_mgr->ioc);
qio_channel_set_name(QIO_CHANNEL(sioc), "pr-manager-helper");
qio_channel_socket_connect_sync(sioc,
&saddr,
&local_err);
g_free(path);
if (local_err) {
object_unref(OBJECT(sioc));
error_propagate(errp, local_err);
return -ENOTCONN;
}
qio_channel_set_delay(QIO_CHANNEL(sioc), false);
pr_mgr->ioc = QIO_CHANNEL(sioc);
/* A simple feature negotation protocol, even though there is
* no optional feature right now.
*/
r = pr_manager_helper_read(pr_mgr, &flags, sizeof(flags), errp);
if (r < 0) {
goto out_close;
}
flags = 0;
r = pr_manager_helper_write(pr_mgr, -1, &flags, sizeof(flags), errp);
if (r < 0) {
goto out_close;
}
return 0;
out_close:
object_unref(OBJECT(pr_mgr->ioc));
pr_mgr->ioc = NULL;
return r;
}
static int pr_manager_helper_run(PRManager *p,
int fd, struct sg_io_hdr *io_hdr)
{
PRManagerHelper *pr_mgr = PR_MANAGER_HELPER(p);
uint32_t len;
PRHelperResponse resp;
int ret;
int expected_dir;
int attempts;
uint8_t cdb[PR_HELPER_CDB_SIZE] = { 0 };
if (!io_hdr->cmd_len || io_hdr->cmd_len > PR_HELPER_CDB_SIZE) {
return -EINVAL;
}
memcpy(cdb, io_hdr->cmdp, io_hdr->cmd_len);
assert(cdb[0] == PERSISTENT_RESERVE_OUT || cdb[0] == PERSISTENT_RESERVE_IN);
expected_dir =
(cdb[0] == PERSISTENT_RESERVE_OUT ? SG_DXFER_TO_DEV : SG_DXFER_FROM_DEV);
if (io_hdr->dxfer_direction != expected_dir) {
return -EINVAL;
}
len = scsi_cdb_xfer(cdb);
if (io_hdr->dxfer_len < len || len > PR_HELPER_DATA_SIZE) {
return -EINVAL;
}
qemu_mutex_lock(&pr_mgr->lock);
/* Try to reconnect while sending the CDB. */
for (attempts = 0; attempts < PR_MAX_RECONNECT_ATTEMPTS; attempts++) {
if (!pr_mgr->ioc) {
ret = pr_manager_helper_initialize(pr_mgr, NULL);
if (ret < 0) {
qemu_mutex_unlock(&pr_mgr->lock);
g_usleep(G_USEC_PER_SEC);
qemu_mutex_lock(&pr_mgr->lock);
continue;
}
}
ret = pr_manager_helper_write(pr_mgr, fd, cdb, ARRAY_SIZE(cdb), NULL);
if (ret >= 0) {
break;
}
}
if (ret < 0) {
goto out;
}
/* After sending the CDB, any communications failure causes the
* command to fail. The failure is transient, retrying the command
* will invoke pr_manager_helper_initialize again.
*/
if (expected_dir == SG_DXFER_TO_DEV) {
io_hdr->resid = io_hdr->dxfer_len - len;
ret = pr_manager_helper_write(pr_mgr, -1, io_hdr->dxferp, len, NULL);
if (ret < 0) {
goto out;
}
}
ret = pr_manager_helper_read(pr_mgr, &resp, sizeof(resp), NULL);
if (ret < 0) {
goto out;
}
resp.result = be32_to_cpu(resp.result);
resp.sz = be32_to_cpu(resp.sz);
if (io_hdr->dxfer_direction == SG_DXFER_FROM_DEV) {
assert(resp.sz <= io_hdr->dxfer_len);
ret = pr_manager_helper_read(pr_mgr, io_hdr->dxferp, resp.sz, NULL);
if (ret < 0) {
goto out;
}
io_hdr->resid = io_hdr->dxfer_len - resp.sz;
} else {
assert(resp.sz == 0);
}
io_hdr->status = resp.result;
if (resp.result == CHECK_CONDITION) {
io_hdr->driver_status = SG_ERR_DRIVER_SENSE;
io_hdr->sb_len_wr = MIN(io_hdr->mx_sb_len, PR_HELPER_SENSE_SIZE);
memcpy(io_hdr->sbp, resp.sense, io_hdr->sb_len_wr);
}
out:
if (ret < 0) {
int sense_len = scsi_build_sense(io_hdr->sbp,
SENSE_CODE(LUN_COMM_FAILURE));
io_hdr->driver_status = SG_ERR_DRIVER_SENSE;
io_hdr->sb_len_wr = MIN(io_hdr->mx_sb_len, sense_len);
io_hdr->status = CHECK_CONDITION;
}
qemu_mutex_unlock(&pr_mgr->lock);
return ret;
}
static void pr_manager_helper_complete(UserCreatable *uc, Error **errp)
{
PRManagerHelper *pr_mgr = PR_MANAGER_HELPER(uc);
qemu_mutex_lock(&pr_mgr->lock);
pr_manager_helper_initialize(pr_mgr, errp);
qemu_mutex_unlock(&pr_mgr->lock);
}
static char *get_path(Object *obj, Error **errp)
{
PRManagerHelper *pr_mgr = PR_MANAGER_HELPER(obj);
return g_strdup(pr_mgr->path);
}
static void set_path(Object *obj, const char *str, Error **errp)
{
PRManagerHelper *pr_mgr = PR_MANAGER_HELPER(obj);
g_free(pr_mgr->path);
pr_mgr->path = g_strdup(str);
}
static void pr_manager_helper_instance_finalize(Object *obj)
{
PRManagerHelper *pr_mgr = PR_MANAGER_HELPER(obj);
object_unref(OBJECT(pr_mgr->ioc));
qemu_mutex_destroy(&pr_mgr->lock);
}
static void pr_manager_helper_instance_init(Object *obj)
{
PRManagerHelper *pr_mgr = PR_MANAGER_HELPER(obj);
qemu_mutex_init(&pr_mgr->lock);
}
static void pr_manager_helper_class_init(ObjectClass *klass,
void *class_data G_GNUC_UNUSED)
{
PRManagerClass *prmgr_klass = PR_MANAGER_CLASS(klass);
UserCreatableClass *uc_klass = USER_CREATABLE_CLASS(klass);
object_class_property_add_str(klass, "path", get_path, set_path,
&error_abort);
uc_klass->complete = pr_manager_helper_complete;
prmgr_klass->run = pr_manager_helper_run;
}
static const TypeInfo pr_manager_helper_info = {
.parent = TYPE_PR_MANAGER,
.name = TYPE_PR_MANAGER_HELPER,
.instance_size = sizeof(PRManagerHelper),
.instance_init = pr_manager_helper_instance_init,
.instance_finalize = pr_manager_helper_instance_finalize,
.class_init = pr_manager_helper_class_init,
};
static void pr_manager_helper_register_types(void)
{
type_register_static(&pr_manager_helper_info);
}
type_init(pr_manager_helper_register_types);

109
scsi/pr-manager.c Normal file
View File

@ -0,0 +1,109 @@
/*
* Persistent reservation manager abstract class
*
* Copyright (c) 2017 Red Hat, Inc.
*
* Author: Paolo Bonzini <pbonzini@redhat.com>
*
* This code is licensed under the LGPL.
*
*/
#include "qemu/osdep.h"
#include <scsi/sg.h>
#include "qapi/error.h"
#include "block/aio.h"
#include "block/thread-pool.h"
#include "scsi/pr-manager.h"
#include "trace.h"
typedef struct PRManagerData {
PRManager *pr_mgr;
struct sg_io_hdr *hdr;
int fd;
} PRManagerData;
static int pr_manager_worker(void *opaque)
{
PRManagerData *data = opaque;
PRManager *pr_mgr = data->pr_mgr;
PRManagerClass *pr_mgr_class =
PR_MANAGER_GET_CLASS(pr_mgr);
struct sg_io_hdr *hdr = data->hdr;
int fd = data->fd;
int r;
g_free(data);
trace_pr_manager_run(fd, hdr->cmdp[0], hdr->cmdp[1]);
/* The reference was taken in pr_manager_execute. */
r = pr_mgr_class->run(pr_mgr, fd, hdr);
object_unref(OBJECT(pr_mgr));
return r;
}
BlockAIOCB *pr_manager_execute(PRManager *pr_mgr,
AioContext *ctx, int fd,
struct sg_io_hdr *hdr,
BlockCompletionFunc *complete,
void *opaque)
{
PRManagerData *data = g_new(PRManagerData, 1);
ThreadPool *pool = aio_get_thread_pool(ctx);
trace_pr_manager_execute(fd, hdr->cmdp[0], hdr->cmdp[1], opaque);
data->pr_mgr = pr_mgr;
data->fd = fd;
data->hdr = hdr;
/* The matching object_unref is in pr_manager_worker. */
object_ref(OBJECT(pr_mgr));
return thread_pool_submit_aio(pool, pr_manager_worker,
data, complete, opaque);
}
static const TypeInfo pr_manager_info = {
.parent = TYPE_OBJECT,
.name = TYPE_PR_MANAGER,
.class_size = sizeof(PRManagerClass),
.abstract = true,
.interfaces = (InterfaceInfo[]) {
{ TYPE_USER_CREATABLE },
{ }
}
};
PRManager *pr_manager_lookup(const char *id, Error **errp)
{
Object *obj;
PRManager *pr_mgr;
obj = object_resolve_path_component(object_get_objects_root(), id);
if (!obj) {
error_setg(errp, "No persistent reservation manager with id '%s'", id);
return NULL;
}
pr_mgr = (PRManager *)
object_dynamic_cast(obj,
TYPE_PR_MANAGER);
if (!pr_mgr) {
error_setg(errp,
"Object with id '%s' is not a persistent reservation manager",
id);
return NULL;
}
return pr_mgr;
}
static void
pr_manager_register_types(void)
{
type_register_static(&pr_manager_info);
}
type_init(pr_manager_register_types);

1075
scsi/qemu-pr-helper.c Normal file

File diff suppressed because it is too large Load Diff

3
scsi/trace-events Normal file
View File

@ -0,0 +1,3 @@
# scsi/pr-manager.c
pr_manager_execute(int fd, int cmd, int sa, void *opaque) "fd=%d cmd=0x%02x service action=0x%02x opaque=%p"
pr_manager_run(int fd, int cmd, int sa) "fd=%d cmd=0x%02x service action=0x%02x"

View File

@ -206,6 +206,11 @@ const struct SCSISense sense_code_OVERLAPPED_COMMANDS = {
.key = ABORTED_COMMAND, .asc = 0x4e, .ascq = 0x00
};
/* Command aborted, LUN Communication Failure */
const struct SCSISense sense_code_LUN_COMM_FAILURE = {
.key = ABORTED_COMMAND, .asc = 0x08, .ascq = 0x00
};
/* Unit attention, Capacity data has changed */
const struct SCSISense sense_code_CAPACITY_CHANGED = {
.key = UNIT_ATTENTION, .asc = 0x2a, .ascq = 0x09
@ -216,6 +221,11 @@ const struct SCSISense sense_code_RESET = {
.key = UNIT_ATTENTION, .asc = 0x29, .ascq = 0x00
};
/* Unit attention, SCSI bus reset */
const struct SCSISense sense_code_SCSI_BUS_RESET = {
.key = UNIT_ATTENTION, .asc = 0x29, .ascq = 0x02
};
/* Unit attention, No medium */
const struct SCSISense sense_code_UNIT_ATTENTION_NO_MEDIUM = {
.key = UNIT_ATTENTION, .asc = 0x3a, .ascq = 0x00

View File

@ -691,6 +691,9 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
CPUARMState *env = &cpu->env;
int pagebits;
Error *local_err = NULL;
#ifndef CONFIG_USER_ONLY
AddressSpace *as;
#endif
cpu_exec_realizefn(cs, &local_err);
if (local_err != NULL) {
@ -881,24 +884,21 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
#ifndef CONFIG_USER_ONLY
if (cpu->has_el3 || arm_feature(env, ARM_FEATURE_M_SECURITY)) {
AddressSpace *as;
as = g_new0(AddressSpace, 1);
cs->num_ases = 2;
if (!cpu->secure_memory) {
cpu->secure_memory = cs->memory;
}
as = address_space_init_shareable(cpu->secure_memory,
"cpu-secure-memory");
address_space_init(as, cpu->secure_memory, "cpu-secure-memory");
cpu_address_space_init(cs, as, ARMASIdx_S);
} else {
cs->num_ases = 1;
}
cpu_address_space_init(cs,
address_space_init_shareable(cs->memory,
"cpu-memory"),
ARMASIdx_NS);
as = g_new0(AddressSpace, 1);
address_space_init(as, cs->memory, "cpu-memory");
cpu_address_space_init(cs, as, ARMASIdx_NS);
#endif
qemu_init_vcpu(cs);

View File

@ -3738,10 +3738,11 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
#ifndef CONFIG_USER_ONLY
if (tcg_enabled()) {
AddressSpace *as_normal = address_space_init_shareable(cs->memory,
"cpu-memory");
AddressSpace *as_normal = g_new0(AddressSpace, 1);
AddressSpace *as_smm = g_new(AddressSpace, 1);
address_space_init(as_normal, cs->memory, "cpu-memory");
cpu->cpu_as_mem = g_new(MemoryRegion, 1);
cpu->cpu_as_root = g_new(MemoryRegion, 1);

View File

@ -64,6 +64,9 @@ memory_region_tb_read(int cpu_index, uint64_t addr, uint64_t value, unsigned siz
memory_region_tb_write(int cpu_index, uint64_t addr, uint64_t value, unsigned size) "cpu %d addr 0x%"PRIx64" value 0x%"PRIx64" size %u"
memory_region_ram_device_read(int cpu_index, void *mr, uint64_t addr, uint64_t value, unsigned size) "cpu %d mr %p addr 0x%"PRIx64" value 0x%"PRIx64" size %u"
memory_region_ram_device_write(int cpu_index, void *mr, uint64_t addr, uint64_t value, unsigned size) "cpu %d mr %p addr 0x%"PRIx64" value 0x%"PRIx64" size %u"
flatview_new(FlatView *view, MemoryRegion *root) "%p (root %p)"
flatview_destroy(FlatView *view, MemoryRegion *root) "%p (root %p)"
flatview_destroy_rcu(FlatView *view, MemoryRegion *root) "%p (root %p)"
### Guest events, keep at bottom

3
vl.c
View File

@ -2889,7 +2889,8 @@ static int machine_set_property(void *opaque,
*/
static bool object_create_initial(const char *type)
{
if (g_str_equal(type, "rng-egd")) {
if (g_str_equal(type, "rng-egd") ||
g_str_has_prefix(type, "pr-manager-")) {
return false;
}