* 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:
		
						commit
						460b6c8e58
					
				
							
								
								
									
										7
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								Makefile
									
									
									
									
									
								
							| @ -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 | ||||
|  | ||||
| @ -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) | ||||
| 
 | ||||
|  | ||||
| @ -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) { | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
| @ -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); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -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); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -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); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -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
									
									
								
							
							
						
						
									
										60
									
								
								configure
									
									
									
									
										vendored
									
									
								
							| @ -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
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								cpus.c
									
									
									
									
									
								
							| @ -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); | ||||
|     } | ||||
|  | ||||
| @ -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: | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										83
									
								
								docs/interop/pr-helper.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								docs/interop/pr-helper.rst
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										111
									
								
								docs/pr-manager.rst
									
									
									
									
									
										Normal 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
									
									
									
									
									
								
							
							
						
						
									
										330
									
								
								exec.c
									
									
									
									
									
								
							| @ -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(§ion->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 | ||||
|  | ||||
| @ -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, | ||||
|     }, | ||||
| 
 | ||||
|  | ||||
| @ -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.  */ | ||||
|  | ||||
| @ -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; | ||||
| } | ||||
|  | ||||
| @ -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) | ||||
|  | ||||
| @ -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(); | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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 | ||||
|  * | ||||
|  | ||||
| @ -21,7 +21,7 @@ typedef struct { | ||||
|     SysBusDevice parent_obj; | ||||
|     /*< public >*/ | ||||
| 
 | ||||
|     AddressSpace *source_as; | ||||
|     AddressSpace source_as; | ||||
|     MemoryRegion iomem; | ||||
|     uint32_t base; | ||||
|     MemoryRegion *source_memory; | ||||
|  | ||||
| @ -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); | ||||
| 
 | ||||
|  | ||||
| @ -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 */ | ||||
|  | ||||
| @ -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
									
								
							
							
						
						
									
										56
									
								
								include/scsi/pr-manager.h
									
									
									
									
									
										Normal 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 | ||||
| @ -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
									
									
									
									
									
								
							
							
						
						
									
										382
									
								
								memory.c
									
									
									
									
									
								
							| @ -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; | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -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) | ||||
|  | ||||
| @ -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' } } | ||||
| 
 | ||||
|  | ||||
| @ -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
									
								
							
							
						
						
									
										41
									
								
								scsi/pr-helper.h
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										302
									
								
								scsi/pr-manager-helper.c
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										109
									
								
								scsi/pr-manager.c
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										1075
									
								
								scsi/qemu-pr-helper.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										3
									
								
								scsi/trace-events
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								scsi/trace-events
									
									
									
									
									
										Normal 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" | ||||
							
								
								
									
										10
									
								
								scsi/utils.c
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								scsi/utils.c
									
									
									
									
									
								
							| @ -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 | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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); | ||||
| 
 | ||||
|  | ||||
| @ -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 | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Peter Maydell
						Peter Maydell