mirror of https://github.com/xemu-project/xemu.git
* docs/atomics fixes and atomic_rcu_* optimization (Emilio)
* NBD bugfix (Eric) * Memory fixes and cleanups (Paolo, Paul) * scsi-block support for SCSI status, including persistent reservations (Paolo) * kvm_stat moves to the Linux repository * SCSI bug fixes (Peter, Prasad) * Killing qemu_char_get_next_serial, non-ARM parts (Xiaoqiang) -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQEcBAABCAAGBQJXSpYYAAoJEL/70l94x66DZ5UIAJiFDjWdeFw5eS+MgjflcjN9 zibrg+z2QFqFP7Z3FlQR5EjiML0g1S1eceUPBCyZazx6bQS3zvHkbhIWVx2RtHeI Rtt5SYRF+R1cvSEJrQGPr3ysCLAThBFuQRayJRQHi/emiUNzfTSW1z0YGaaErimP o4vF6Sjt1RQcnBkOPw6Qcoe4gSIbh9W4vQFbaZdB2KoWM9fOYBrjFgEFm1lo01vS Da77vlr1dcYCjohNgo9BOZIOllTQK7WG1XWRE6HPbqa8w6oePXikxJ1lc4PQxXyT clZy5uGJicp7QmEHf+yG8Evtm2mZG2iOEF4NbbFIm/GghOdFzwtHavxU/ahpAig= =DaAU -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/bonzini/tags/for-upstream' into staging * docs/atomics fixes and atomic_rcu_* optimization (Emilio) * NBD bugfix (Eric) * Memory fixes and cleanups (Paolo, Paul) * scsi-block support for SCSI status, including persistent reservations (Paolo) * kvm_stat moves to the Linux repository * SCSI bug fixes (Peter, Prasad) * Killing qemu_char_get_next_serial, non-ARM parts (Xiaoqiang) # gpg: Signature made Sun 29 May 2016 08:11:20 BST using RSA key ID 78C7AE83 # gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>" # gpg: aka "Paolo Bonzini <pbonzini@redhat.com>" * remotes/bonzini/tags/for-upstream: (30 commits) exec: hide mr->ram_addr from qemu_get_ram_ptr users memory: split memory_region_from_host from qemu_ram_addr_from_host exec: remove ram_addr argument from qemu_ram_block_from_host memory: remove qemu_get_ram_fd, qemu_set_ram_fd, qemu_ram_block_host_ptr scsi-generic: Merge block max xfer len in INQUIRY response scsi-block: always use SG_IO scsi-disk: introduce scsi_disk_req_check_error scsi-disk: add need_fua_emulation to SCSIDiskClass scsi-disk: introduce dma_readv and dma_writev scsi-disk: introduce a common base class xen-hvm: ignore background I/O sections docs/atomics: update comparison with Linux atomics: do not emit consume barrier for atomic_rcu_read atomics: emit an smp_read_barrier_depends() barrier only for Alpha and Thread Sanitizer docs/atomics: update atomic_read/set comparison with Linux bt: rewrite csrhci_write to avoid out-of-bounds writes block/iscsi: avoid potential overflow of acb->task->cdb scsi: megasas: check 'read_queue_head' index value scsi: megasas: initialise local configuration data buffer scsi: megasas: use appropriate property buffer size ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
07e070aac4
9
Makefile
9
Makefile
|
@ -92,9 +92,6 @@ HELPERS-$(CONFIG_LINUX) = qemu-bridge-helper$(EXESUF)
|
||||||
ifdef BUILD_DOCS
|
ifdef BUILD_DOCS
|
||||||
DOCS=qemu-doc.html qemu-tech.html qemu.1 qemu-img.1 qemu-nbd.8 qemu-ga.8
|
DOCS=qemu-doc.html qemu-tech.html qemu.1 qemu-img.1 qemu-nbd.8 qemu-ga.8
|
||||||
DOCS+=qmp-commands.txt
|
DOCS+=qmp-commands.txt
|
||||||
ifdef CONFIG_LINUX
|
|
||||||
DOCS+=kvm_stat.1
|
|
||||||
endif
|
|
||||||
ifdef CONFIG_VIRTFS
|
ifdef CONFIG_VIRTFS
|
||||||
DOCS+=fsdev/virtfs-proxy-helper.1
|
DOCS+=fsdev/virtfs-proxy-helper.1
|
||||||
endif
|
endif
|
||||||
|
@ -571,12 +568,6 @@ qemu-ga.8: qemu-ga.texi
|
||||||
$(POD2MAN) --section=8 --center=" " --release=" " qemu-ga.pod > $@, \
|
$(POD2MAN) --section=8 --center=" " --release=" " qemu-ga.pod > $@, \
|
||||||
" GEN $@")
|
" GEN $@")
|
||||||
|
|
||||||
kvm_stat.1: scripts/kvm/kvm_stat.texi
|
|
||||||
$(call quiet-command, \
|
|
||||||
perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< kvm_stat.pod && \
|
|
||||||
$(POD2MAN) --section=1 --center=" " --release=" " kvm_stat.pod > $@, \
|
|
||||||
" GEN $@")
|
|
||||||
|
|
||||||
dvi: qemu-doc.dvi qemu-tech.dvi
|
dvi: qemu-doc.dvi qemu-tech.dvi
|
||||||
html: qemu-doc.html qemu-tech.html
|
html: qemu-doc.html qemu-tech.html
|
||||||
info: qemu-doc.info qemu-tech.info
|
info: qemu-doc.info qemu-tech.info
|
||||||
|
|
|
@ -833,6 +833,13 @@ static BlockAIOCB *iscsi_aio_ioctl(BlockDriverState *bs,
|
||||||
return &acb->common;
|
return &acb->common;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (acb->ioh->cmd_len > SCSI_CDB_MAX_SIZE) {
|
||||||
|
error_report("iSCSI: ioctl error CDB exceeds max size (%d > %d)",
|
||||||
|
acb->ioh->cmd_len, SCSI_CDB_MAX_SIZE);
|
||||||
|
qemu_aio_unref(acb);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
acb->task = malloc(sizeof(struct scsi_task));
|
acb->task = malloc(sizeof(struct scsi_task));
|
||||||
if (acb->task == NULL) {
|
if (acb->task == NULL) {
|
||||||
error_report("iSCSI: Failed to allocate task for scsi command. %s",
|
error_report("iSCSI: Failed to allocate task for scsi command. %s",
|
||||||
|
|
3
cputlb.c
3
cputlb.c
|
@ -246,7 +246,8 @@ static inline ram_addr_t qemu_ram_addr_from_host_nofail(void *ptr)
|
||||||
{
|
{
|
||||||
ram_addr_t ram_addr;
|
ram_addr_t ram_addr;
|
||||||
|
|
||||||
if (qemu_ram_addr_from_host(ptr, &ram_addr) == NULL) {
|
ram_addr = qemu_ram_addr_from_host(ptr);
|
||||||
|
if (ram_addr == RAM_ADDR_INVALID) {
|
||||||
fprintf(stderr, "Bad ram pointer %p\n", ptr);
|
fprintf(stderr, "Bad ram pointer %p\n", ptr);
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
|
@ -326,21 +326,41 @@ and memory barriers, and the equivalents in QEMU:
|
||||||
use a boxed atomic_t type; atomic operations in QEMU are polymorphic
|
use a boxed atomic_t type; atomic operations in QEMU are polymorphic
|
||||||
and use normal C types.
|
and use normal C types.
|
||||||
|
|
||||||
- atomic_read and atomic_set in Linux give no guarantee at all;
|
- Originally, atomic_read and atomic_set in Linux gave no guarantee
|
||||||
atomic_read and atomic_set in QEMU include a compiler barrier
|
at all. Linux 4.1 updated them to implement volatile
|
||||||
(similar to the READ_ONCE/WRITE_ONCE macros in Linux).
|
semantics via ACCESS_ONCE (or the more recent READ/WRITE_ONCE).
|
||||||
|
|
||||||
- most atomic read-modify-write operations in Linux return void;
|
QEMU's atomic_read/set implement, if the compiler supports it, C11
|
||||||
in QEMU, all of them return the old value of the variable.
|
atomic relaxed semantics, and volatile semantics otherwise.
|
||||||
|
Both semantics prevent the compiler from doing certain transformations;
|
||||||
|
the difference is that atomic accesses are guaranteed to be atomic,
|
||||||
|
while volatile accesses aren't. Thus, in the volatile case we just cross
|
||||||
|
our fingers hoping that the compiler will generate atomic accesses,
|
||||||
|
since we assume the variables passed are machine-word sized and
|
||||||
|
properly aligned.
|
||||||
|
No barriers are implied by atomic_read/set in either Linux or QEMU.
|
||||||
|
|
||||||
|
- atomic read-modify-write operations in Linux are of three kinds:
|
||||||
|
|
||||||
|
atomic_OP returns void
|
||||||
|
atomic_OP_return returns new value of the variable
|
||||||
|
atomic_fetch_OP returns the old value of the variable
|
||||||
|
atomic_cmpxchg returns the old value of the variable
|
||||||
|
|
||||||
|
In QEMU, the second kind does not exist. Currently Linux has
|
||||||
|
atomic_fetch_or only. QEMU provides and, or, inc, dec, add, sub.
|
||||||
|
|
||||||
- different atomic read-modify-write operations in Linux imply
|
- different atomic read-modify-write operations in Linux imply
|
||||||
a different set of memory barriers; in QEMU, all of them enforce
|
a different set of memory barriers; in QEMU, all of them enforce
|
||||||
sequential consistency, which means they imply full memory barriers
|
sequential consistency, which means they imply full memory barriers
|
||||||
before and after the operation.
|
before and after the operation.
|
||||||
|
|
||||||
- Linux does not have an equivalent of atomic_mb_read() and
|
- Linux does not have an equivalent of atomic_mb_set(). In particular,
|
||||||
atomic_mb_set(). In particular, note that set_mb() is a little
|
note that smp_store_mb() is a little weaker than atomic_mb_set().
|
||||||
weaker than atomic_mb_set().
|
atomic_mb_read() compiles to the same instructions as Linux's
|
||||||
|
smp_load_acquire(), but this should be treated as an implementation
|
||||||
|
detail. If required, QEMU might later add atomic_load_acquire() and
|
||||||
|
atomic_store_release() macros.
|
||||||
|
|
||||||
|
|
||||||
SOURCES
|
SOURCES
|
||||||
|
|
110
exec.c
110
exec.c
|
@ -1815,40 +1815,6 @@ void qemu_ram_remap(ram_addr_t addr, ram_addr_t length)
|
||||||
}
|
}
|
||||||
#endif /* !_WIN32 */
|
#endif /* !_WIN32 */
|
||||||
|
|
||||||
int qemu_get_ram_fd(ram_addr_t addr)
|
|
||||||
{
|
|
||||||
RAMBlock *block;
|
|
||||||
int fd;
|
|
||||||
|
|
||||||
rcu_read_lock();
|
|
||||||
block = qemu_get_ram_block(addr);
|
|
||||||
fd = block->fd;
|
|
||||||
rcu_read_unlock();
|
|
||||||
return fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
void qemu_set_ram_fd(ram_addr_t addr, int fd)
|
|
||||||
{
|
|
||||||
RAMBlock *block;
|
|
||||||
|
|
||||||
rcu_read_lock();
|
|
||||||
block = qemu_get_ram_block(addr);
|
|
||||||
block->fd = fd;
|
|
||||||
rcu_read_unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
void *qemu_get_ram_block_host_ptr(ram_addr_t addr)
|
|
||||||
{
|
|
||||||
RAMBlock *block;
|
|
||||||
void *ptr;
|
|
||||||
|
|
||||||
rcu_read_lock();
|
|
||||||
block = qemu_get_ram_block(addr);
|
|
||||||
ptr = ramblock_ptr(block, 0);
|
|
||||||
rcu_read_unlock();
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return a host pointer to ram allocated with qemu_ram_alloc.
|
/* Return a host pointer to ram allocated with qemu_ram_alloc.
|
||||||
* This should not be used for general purpose DMA. Use address_space_map
|
* This should not be used for general purpose DMA. Use address_space_map
|
||||||
* or address_space_rw instead. For local memory (e.g. video ram) that the
|
* or address_space_rw instead. For local memory (e.g. video ram) that the
|
||||||
|
@ -1856,12 +1822,13 @@ void *qemu_get_ram_block_host_ptr(ram_addr_t addr)
|
||||||
*
|
*
|
||||||
* Called within RCU critical section.
|
* Called within RCU critical section.
|
||||||
*/
|
*/
|
||||||
void *qemu_get_ram_ptr(RAMBlock *ram_block, ram_addr_t addr)
|
void *qemu_map_ram_ptr(RAMBlock *ram_block, ram_addr_t addr)
|
||||||
{
|
{
|
||||||
RAMBlock *block = ram_block;
|
RAMBlock *block = ram_block;
|
||||||
|
|
||||||
if (block == NULL) {
|
if (block == NULL) {
|
||||||
block = qemu_get_ram_block(addr);
|
block = qemu_get_ram_block(addr);
|
||||||
|
addr -= block->offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (xen_enabled() && block->host == NULL) {
|
if (xen_enabled() && block->host == NULL) {
|
||||||
|
@ -1875,10 +1842,10 @@ void *qemu_get_ram_ptr(RAMBlock *ram_block, ram_addr_t addr)
|
||||||
|
|
||||||
block->host = xen_map_cache(block->offset, block->max_length, 1);
|
block->host = xen_map_cache(block->offset, block->max_length, 1);
|
||||||
}
|
}
|
||||||
return ramblock_ptr(block, addr - block->offset);
|
return ramblock_ptr(block, addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return a host pointer to guest's ram. Similar to qemu_get_ram_ptr
|
/* Return a host pointer to guest's ram. Similar to qemu_map_ram_ptr
|
||||||
* but takes a size argument.
|
* but takes a size argument.
|
||||||
*
|
*
|
||||||
* Called within RCU critical section.
|
* Called within RCU critical section.
|
||||||
|
@ -1887,16 +1854,15 @@ static void *qemu_ram_ptr_length(RAMBlock *ram_block, ram_addr_t addr,
|
||||||
hwaddr *size)
|
hwaddr *size)
|
||||||
{
|
{
|
||||||
RAMBlock *block = ram_block;
|
RAMBlock *block = ram_block;
|
||||||
ram_addr_t offset_inside_block;
|
|
||||||
if (*size == 0) {
|
if (*size == 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (block == NULL) {
|
if (block == NULL) {
|
||||||
block = qemu_get_ram_block(addr);
|
block = qemu_get_ram_block(addr);
|
||||||
|
addr -= block->offset;
|
||||||
}
|
}
|
||||||
offset_inside_block = addr - block->offset;
|
*size = MIN(*size, block->max_length - addr);
|
||||||
*size = MIN(*size, block->max_length - offset_inside_block);
|
|
||||||
|
|
||||||
if (xen_enabled() && block->host == NULL) {
|
if (xen_enabled() && block->host == NULL) {
|
||||||
/* We need to check if the requested address is in the RAM
|
/* We need to check if the requested address is in the RAM
|
||||||
|
@ -1910,7 +1876,7 @@ static void *qemu_ram_ptr_length(RAMBlock *ram_block, ram_addr_t addr,
|
||||||
block->host = xen_map_cache(block->offset, block->max_length, 1);
|
block->host = xen_map_cache(block->offset, block->max_length, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ramblock_ptr(block, offset_inside_block);
|
return ramblock_ptr(block, addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1931,16 +1897,16 @@ static void *qemu_ram_ptr_length(RAMBlock *ram_block, ram_addr_t addr,
|
||||||
* ram_addr_t.
|
* ram_addr_t.
|
||||||
*/
|
*/
|
||||||
RAMBlock *qemu_ram_block_from_host(void *ptr, bool round_offset,
|
RAMBlock *qemu_ram_block_from_host(void *ptr, bool round_offset,
|
||||||
ram_addr_t *ram_addr,
|
|
||||||
ram_addr_t *offset)
|
ram_addr_t *offset)
|
||||||
{
|
{
|
||||||
RAMBlock *block;
|
RAMBlock *block;
|
||||||
uint8_t *host = ptr;
|
uint8_t *host = ptr;
|
||||||
|
|
||||||
if (xen_enabled()) {
|
if (xen_enabled()) {
|
||||||
|
ram_addr_t ram_addr;
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
*ram_addr = xen_ram_addr_from_mapcache(ptr);
|
ram_addr = xen_ram_addr_from_mapcache(ptr);
|
||||||
block = qemu_get_ram_block(*ram_addr);
|
block = qemu_get_ram_block(ram_addr);
|
||||||
if (block) {
|
if (block) {
|
||||||
*offset = (host - block->host);
|
*offset = (host - block->host);
|
||||||
}
|
}
|
||||||
|
@ -1972,7 +1938,6 @@ found:
|
||||||
if (round_offset) {
|
if (round_offset) {
|
||||||
*offset &= TARGET_PAGE_MASK;
|
*offset &= TARGET_PAGE_MASK;
|
||||||
}
|
}
|
||||||
*ram_addr = block->offset + *offset;
|
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
|
@ -1999,18 +1964,17 @@ RAMBlock *qemu_ram_block_by_name(const char *name)
|
||||||
|
|
||||||
/* Some of the softmmu routines need to translate from a host pointer
|
/* Some of the softmmu routines need to translate from a host pointer
|
||||||
(typically a TLB entry) back to a ram offset. */
|
(typically a TLB entry) back to a ram offset. */
|
||||||
MemoryRegion *qemu_ram_addr_from_host(void *ptr, ram_addr_t *ram_addr)
|
ram_addr_t qemu_ram_addr_from_host(void *ptr)
|
||||||
{
|
{
|
||||||
RAMBlock *block;
|
RAMBlock *block;
|
||||||
ram_addr_t offset; /* Not used */
|
ram_addr_t offset;
|
||||||
|
|
||||||
block = qemu_ram_block_from_host(ptr, false, ram_addr, &offset);
|
|
||||||
|
|
||||||
|
block = qemu_ram_block_from_host(ptr, false, &offset);
|
||||||
if (!block) {
|
if (!block) {
|
||||||
return NULL;
|
return RAM_ADDR_INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
return block->mr;
|
return block->offset + offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Called within RCU critical section. */
|
/* Called within RCU critical section. */
|
||||||
|
@ -2022,13 +1986,13 @@ static void notdirty_mem_write(void *opaque, hwaddr ram_addr,
|
||||||
}
|
}
|
||||||
switch (size) {
|
switch (size) {
|
||||||
case 1:
|
case 1:
|
||||||
stb_p(qemu_get_ram_ptr(NULL, ram_addr), val);
|
stb_p(qemu_map_ram_ptr(NULL, ram_addr), val);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
stw_p(qemu_get_ram_ptr(NULL, ram_addr), val);
|
stw_p(qemu_map_ram_ptr(NULL, ram_addr), val);
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
stl_p(qemu_get_ram_ptr(NULL, ram_addr), val);
|
stl_p(qemu_map_ram_ptr(NULL, ram_addr), val);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
abort();
|
abort();
|
||||||
|
@ -2490,6 +2454,8 @@ static void invalidate_and_set_dirty(MemoryRegion *mr, hwaddr addr,
|
||||||
hwaddr length)
|
hwaddr length)
|
||||||
{
|
{
|
||||||
uint8_t dirty_log_mask = memory_region_get_dirty_log_mask(mr);
|
uint8_t dirty_log_mask = memory_region_get_dirty_log_mask(mr);
|
||||||
|
addr += memory_region_get_ram_addr(mr);
|
||||||
|
|
||||||
/* No early return if dirty_log_mask is or becomes 0, because
|
/* No early return if dirty_log_mask is or becomes 0, because
|
||||||
* cpu_physical_memory_set_dirty_range will still call
|
* cpu_physical_memory_set_dirty_range will still call
|
||||||
* xen_modified_memory.
|
* xen_modified_memory.
|
||||||
|
@ -2602,9 +2568,8 @@ static MemTxResult address_space_write_continue(AddressSpace *as, hwaddr addr,
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
addr1 += memory_region_get_ram_addr(mr);
|
|
||||||
/* RAM case */
|
/* RAM case */
|
||||||
ptr = qemu_get_ram_ptr(mr->ram_block, addr1);
|
ptr = qemu_map_ram_ptr(mr->ram_block, addr1);
|
||||||
memcpy(ptr, buf, l);
|
memcpy(ptr, buf, l);
|
||||||
invalidate_and_set_dirty(mr, addr1, l);
|
invalidate_and_set_dirty(mr, addr1, l);
|
||||||
}
|
}
|
||||||
|
@ -2695,8 +2660,7 @@ MemTxResult address_space_read_continue(AddressSpace *as, hwaddr addr,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* RAM case */
|
/* RAM case */
|
||||||
ptr = qemu_get_ram_ptr(mr->ram_block,
|
ptr = qemu_map_ram_ptr(mr->ram_block, addr1);
|
||||||
memory_region_get_ram_addr(mr) + addr1);
|
|
||||||
memcpy(buf, ptr, l);
|
memcpy(buf, ptr, l);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2779,9 +2743,8 @@ static inline void cpu_physical_memory_write_rom_internal(AddressSpace *as,
|
||||||
memory_region_is_romd(mr))) {
|
memory_region_is_romd(mr))) {
|
||||||
l = memory_access_size(mr, l, addr1);
|
l = memory_access_size(mr, l, addr1);
|
||||||
} else {
|
} else {
|
||||||
addr1 += memory_region_get_ram_addr(mr);
|
|
||||||
/* ROM/RAM case */
|
/* ROM/RAM case */
|
||||||
ptr = qemu_get_ram_ptr(mr->ram_block, addr1);
|
ptr = qemu_map_ram_ptr(mr->ram_block, addr1);
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case WRITE_DATA:
|
case WRITE_DATA:
|
||||||
memcpy(ptr, buf, l);
|
memcpy(ptr, buf, l);
|
||||||
|
@ -2939,7 +2902,6 @@ void *address_space_map(AddressSpace *as,
|
||||||
hwaddr done = 0;
|
hwaddr done = 0;
|
||||||
hwaddr l, xlat, base;
|
hwaddr l, xlat, base;
|
||||||
MemoryRegion *mr, *this_mr;
|
MemoryRegion *mr, *this_mr;
|
||||||
ram_addr_t raddr;
|
|
||||||
void *ptr;
|
void *ptr;
|
||||||
|
|
||||||
if (len == 0) {
|
if (len == 0) {
|
||||||
|
@ -2974,7 +2936,6 @@ void *address_space_map(AddressSpace *as,
|
||||||
}
|
}
|
||||||
|
|
||||||
base = xlat;
|
base = xlat;
|
||||||
raddr = memory_region_get_ram_addr(mr);
|
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
len -= l;
|
len -= l;
|
||||||
|
@ -2993,7 +2954,7 @@ void *address_space_map(AddressSpace *as,
|
||||||
|
|
||||||
memory_region_ref(mr);
|
memory_region_ref(mr);
|
||||||
*plen = done;
|
*plen = done;
|
||||||
ptr = qemu_ram_ptr_length(mr->ram_block, raddr + base, plen);
|
ptr = qemu_ram_ptr_length(mr->ram_block, base, plen);
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
|
|
||||||
return ptr;
|
return ptr;
|
||||||
|
@ -3010,7 +2971,7 @@ void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len,
|
||||||
MemoryRegion *mr;
|
MemoryRegion *mr;
|
||||||
ram_addr_t addr1;
|
ram_addr_t addr1;
|
||||||
|
|
||||||
mr = qemu_ram_addr_from_host(buffer, &addr1);
|
mr = memory_region_from_host(buffer, &addr1);
|
||||||
assert(mr != NULL);
|
assert(mr != NULL);
|
||||||
if (is_write) {
|
if (is_write) {
|
||||||
invalidate_and_set_dirty(mr, addr1, access_len);
|
invalidate_and_set_dirty(mr, addr1, access_len);
|
||||||
|
@ -3077,8 +3038,7 @@ static inline uint32_t address_space_ldl_internal(AddressSpace *as, hwaddr addr,
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
/* RAM case */
|
/* RAM case */
|
||||||
ptr = qemu_get_ram_ptr(mr->ram_block,
|
ptr = qemu_map_ram_ptr(mr->ram_block, addr1);
|
||||||
memory_region_get_ram_addr(mr) + addr1);
|
|
||||||
switch (endian) {
|
switch (endian) {
|
||||||
case DEVICE_LITTLE_ENDIAN:
|
case DEVICE_LITTLE_ENDIAN:
|
||||||
val = ldl_le_p(ptr);
|
val = ldl_le_p(ptr);
|
||||||
|
@ -3171,8 +3131,7 @@ static inline uint64_t address_space_ldq_internal(AddressSpace *as, hwaddr addr,
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
/* RAM case */
|
/* RAM case */
|
||||||
ptr = qemu_get_ram_ptr(mr->ram_block,
|
ptr = qemu_map_ram_ptr(mr->ram_block, addr1);
|
||||||
memory_region_get_ram_addr(mr) + addr1);
|
|
||||||
switch (endian) {
|
switch (endian) {
|
||||||
case DEVICE_LITTLE_ENDIAN:
|
case DEVICE_LITTLE_ENDIAN:
|
||||||
val = ldq_le_p(ptr);
|
val = ldq_le_p(ptr);
|
||||||
|
@ -3285,8 +3244,7 @@ static inline uint32_t address_space_lduw_internal(AddressSpace *as,
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
/* RAM case */
|
/* RAM case */
|
||||||
ptr = qemu_get_ram_ptr(mr->ram_block,
|
ptr = qemu_map_ram_ptr(mr->ram_block, addr1);
|
||||||
memory_region_get_ram_addr(mr) + addr1);
|
|
||||||
switch (endian) {
|
switch (endian) {
|
||||||
case DEVICE_LITTLE_ENDIAN:
|
case DEVICE_LITTLE_ENDIAN:
|
||||||
val = lduw_le_p(ptr);
|
val = lduw_le_p(ptr);
|
||||||
|
@ -3368,13 +3326,13 @@ void address_space_stl_notdirty(AddressSpace *as, hwaddr addr, uint32_t val,
|
||||||
|
|
||||||
r = memory_region_dispatch_write(mr, addr1, val, 4, attrs);
|
r = memory_region_dispatch_write(mr, addr1, val, 4, attrs);
|
||||||
} else {
|
} else {
|
||||||
addr1 += memory_region_get_ram_addr(mr);
|
ptr = qemu_map_ram_ptr(mr->ram_block, addr1);
|
||||||
ptr = qemu_get_ram_ptr(mr->ram_block, addr1);
|
|
||||||
stl_p(ptr, val);
|
stl_p(ptr, val);
|
||||||
|
|
||||||
dirty_log_mask = memory_region_get_dirty_log_mask(mr);
|
dirty_log_mask = memory_region_get_dirty_log_mask(mr);
|
||||||
dirty_log_mask &= ~(1 << DIRTY_MEMORY_CODE);
|
dirty_log_mask &= ~(1 << DIRTY_MEMORY_CODE);
|
||||||
cpu_physical_memory_set_dirty_range(addr1, 4, dirty_log_mask);
|
cpu_physical_memory_set_dirty_range(memory_region_get_ram_addr(mr) + addr,
|
||||||
|
4, dirty_log_mask);
|
||||||
r = MEMTX_OK;
|
r = MEMTX_OK;
|
||||||
}
|
}
|
||||||
if (result) {
|
if (result) {
|
||||||
|
@ -3423,8 +3381,7 @@ static inline void address_space_stl_internal(AddressSpace *as,
|
||||||
r = memory_region_dispatch_write(mr, addr1, val, 4, attrs);
|
r = memory_region_dispatch_write(mr, addr1, val, 4, attrs);
|
||||||
} else {
|
} else {
|
||||||
/* RAM case */
|
/* RAM case */
|
||||||
addr1 += memory_region_get_ram_addr(mr);
|
ptr = qemu_map_ram_ptr(mr->ram_block, addr1);
|
||||||
ptr = qemu_get_ram_ptr(mr->ram_block, addr1);
|
|
||||||
switch (endian) {
|
switch (endian) {
|
||||||
case DEVICE_LITTLE_ENDIAN:
|
case DEVICE_LITTLE_ENDIAN:
|
||||||
stl_le_p(ptr, val);
|
stl_le_p(ptr, val);
|
||||||
|
@ -3533,8 +3490,7 @@ static inline void address_space_stw_internal(AddressSpace *as,
|
||||||
r = memory_region_dispatch_write(mr, addr1, val, 2, attrs);
|
r = memory_region_dispatch_write(mr, addr1, val, 2, attrs);
|
||||||
} else {
|
} else {
|
||||||
/* RAM case */
|
/* RAM case */
|
||||||
addr1 += memory_region_get_ram_addr(mr);
|
ptr = qemu_map_ram_ptr(mr->ram_block, addr1);
|
||||||
ptr = qemu_get_ram_ptr(mr->ram_block, addr1);
|
|
||||||
switch (endian) {
|
switch (endian) {
|
||||||
case DEVICE_LITTLE_ENDIAN:
|
case DEVICE_LITTLE_ENDIAN:
|
||||||
stw_le_p(ptr, val);
|
stw_le_p(ptr, val);
|
||||||
|
|
|
@ -39,9 +39,14 @@ struct csrhci_s {
|
||||||
int out_size;
|
int out_size;
|
||||||
uint8_t outfifo[FIFO_LEN * 2];
|
uint8_t outfifo[FIFO_LEN * 2];
|
||||||
uint8_t inpkt[FIFO_LEN];
|
uint8_t inpkt[FIFO_LEN];
|
||||||
|
enum {
|
||||||
|
CSR_HDR_LEN,
|
||||||
|
CSR_DATA_LEN,
|
||||||
|
CSR_DATA
|
||||||
|
} in_state;
|
||||||
int in_len;
|
int in_len;
|
||||||
int in_hdr;
|
int in_hdr;
|
||||||
int in_data;
|
int in_needed;
|
||||||
QEMUTimer *out_tm;
|
QEMUTimer *out_tm;
|
||||||
int64_t baud_delay;
|
int64_t baud_delay;
|
||||||
|
|
||||||
|
@ -296,38 +301,60 @@ static int csrhci_data_len(const uint8_t *pkt)
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void csrhci_ready_for_next_inpkt(struct csrhci_s *s)
|
||||||
|
{
|
||||||
|
s->in_state = CSR_HDR_LEN;
|
||||||
|
s->in_len = 0;
|
||||||
|
s->in_needed = 2;
|
||||||
|
s->in_hdr = INT_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
static int csrhci_write(struct CharDriverState *chr,
|
static int csrhci_write(struct CharDriverState *chr,
|
||||||
const uint8_t *buf, int len)
|
const uint8_t *buf, int len)
|
||||||
{
|
{
|
||||||
struct csrhci_s *s = (struct csrhci_s *) chr->opaque;
|
struct csrhci_s *s = (struct csrhci_s *) chr->opaque;
|
||||||
int plen = s->in_len;
|
int total = 0;
|
||||||
|
|
||||||
if (!s->enable)
|
if (!s->enable)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
s->in_len += len;
|
for (;;) {
|
||||||
memcpy(s->inpkt + plen, buf, len);
|
int cnt = MIN(len, s->in_needed - s->in_len);
|
||||||
|
if (cnt) {
|
||||||
|
memcpy(s->inpkt + s->in_len, buf, cnt);
|
||||||
|
s->in_len += cnt;
|
||||||
|
buf += cnt;
|
||||||
|
len -= cnt;
|
||||||
|
total += cnt;
|
||||||
|
}
|
||||||
|
|
||||||
while (1) {
|
if (s->in_len < s->in_needed) {
|
||||||
if (s->in_len >= 2 && plen < 2)
|
|
||||||
s->in_hdr = csrhci_header_len(s->inpkt) + 1;
|
|
||||||
|
|
||||||
if (s->in_len >= s->in_hdr && plen < s->in_hdr)
|
|
||||||
s->in_data = csrhci_data_len(s->inpkt) + s->in_hdr;
|
|
||||||
|
|
||||||
if (s->in_len >= s->in_data) {
|
|
||||||
csrhci_in_packet(s, s->inpkt);
|
|
||||||
|
|
||||||
memmove(s->inpkt, s->inpkt + s->in_len, s->in_len - s->in_data);
|
|
||||||
s->in_len -= s->in_data;
|
|
||||||
s->in_hdr = INT_MAX;
|
|
||||||
s->in_data = INT_MAX;
|
|
||||||
plen = 0;
|
|
||||||
} else
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s->in_state == CSR_HDR_LEN) {
|
||||||
|
s->in_hdr = csrhci_header_len(s->inpkt) + 1;
|
||||||
|
assert(s->in_hdr >= s->in_needed);
|
||||||
|
s->in_needed = s->in_hdr;
|
||||||
|
s->in_state = CSR_DATA_LEN;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s->in_state == CSR_DATA_LEN) {
|
||||||
|
s->in_needed += csrhci_data_len(s->inpkt);
|
||||||
|
/* hci_acl_hdr could specify more than 4096 bytes, so assert. */
|
||||||
|
assert(s->in_needed <= sizeof(s->inpkt));
|
||||||
|
s->in_state = CSR_DATA;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s->in_state == CSR_DATA) {
|
||||||
|
csrhci_in_packet(s, s->inpkt);
|
||||||
|
csrhci_ready_for_next_inpkt(s);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return len;
|
return total;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void csrhci_out_hci_packet_event(void *opaque,
|
static void csrhci_out_hci_packet_event(void *opaque,
|
||||||
|
@ -389,11 +416,9 @@ static void csrhci_reset(struct csrhci_s *s)
|
||||||
{
|
{
|
||||||
s->out_len = 0;
|
s->out_len = 0;
|
||||||
s->out_size = FIFO_LEN;
|
s->out_size = FIFO_LEN;
|
||||||
s->in_len = 0;
|
csrhci_ready_for_next_inpkt(s);
|
||||||
s->baud_delay = NANOSECONDS_PER_SECOND;
|
s->baud_delay = NANOSECONDS_PER_SECOND;
|
||||||
s->enable = 0;
|
s->enable = 0;
|
||||||
s->in_hdr = INT_MAX;
|
|
||||||
s->in_data = INT_MAX;
|
|
||||||
|
|
||||||
s->modem_state = 0;
|
s->modem_state = 0;
|
||||||
/* After a while... (but sooner than 10ms) */
|
/* After a while... (but sooner than 10ms) */
|
||||||
|
|
|
@ -983,9 +983,10 @@ void slavio_serial_ms_kbd_init(hwaddr base, qemu_irq irq,
|
||||||
sysbus_mmio_map(s, 0, base);
|
sysbus_mmio_map(s, 0, base);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int escc_init1(SysBusDevice *dev)
|
static void escc_init1(Object *obj)
|
||||||
{
|
{
|
||||||
ESCCState *s = ESCC(dev);
|
ESCCState *s = ESCC(obj);
|
||||||
|
SysBusDevice *dev = SYS_BUS_DEVICE(obj);
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
s->chn[0].disabled = s->disabled;
|
s->chn[0].disabled = s->disabled;
|
||||||
|
@ -994,17 +995,26 @@ static int escc_init1(SysBusDevice *dev)
|
||||||
sysbus_init_irq(dev, &s->chn[i].irq);
|
sysbus_init_irq(dev, &s->chn[i].irq);
|
||||||
s->chn[i].chn = 1 - i;
|
s->chn[i].chn = 1 - i;
|
||||||
s->chn[i].clock = s->frequency / 2;
|
s->chn[i].clock = s->frequency / 2;
|
||||||
|
}
|
||||||
|
s->chn[0].otherchn = &s->chn[1];
|
||||||
|
s->chn[1].otherchn = &s->chn[0];
|
||||||
|
|
||||||
|
memory_region_init_io(&s->mmio, obj, &escc_mem_ops, s, "escc",
|
||||||
|
ESCC_SIZE << s->it_shift);
|
||||||
|
sysbus_init_mmio(dev, &s->mmio);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void escc_realize(DeviceState *dev, Error **errp)
|
||||||
|
{
|
||||||
|
ESCCState *s = ESCC(dev);
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 2; i++) {
|
||||||
if (s->chn[i].chr) {
|
if (s->chn[i].chr) {
|
||||||
qemu_chr_add_handlers(s->chn[i].chr, serial_can_receive,
|
qemu_chr_add_handlers(s->chn[i].chr, serial_can_receive,
|
||||||
serial_receive1, serial_event, &s->chn[i]);
|
serial_receive1, serial_event, &s->chn[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s->chn[0].otherchn = &s->chn[1];
|
|
||||||
s->chn[1].otherchn = &s->chn[0];
|
|
||||||
|
|
||||||
memory_region_init_io(&s->mmio, OBJECT(s), &escc_mem_ops, s, "escc",
|
|
||||||
ESCC_SIZE << s->it_shift);
|
|
||||||
sysbus_init_mmio(dev, &s->mmio);
|
|
||||||
|
|
||||||
if (s->chn[0].type == mouse) {
|
if (s->chn[0].type == mouse) {
|
||||||
qemu_add_mouse_event_handler(sunmouse_event, &s->chn[0], 0,
|
qemu_add_mouse_event_handler(sunmouse_event, &s->chn[0], 0,
|
||||||
|
@ -1014,8 +1024,6 @@ static int escc_init1(SysBusDevice *dev)
|
||||||
s->chn[1].hs = qemu_input_handler_register((DeviceState *)(&s->chn[1]),
|
s->chn[1].hs = qemu_input_handler_register((DeviceState *)(&s->chn[1]),
|
||||||
&sunkbd_handler);
|
&sunkbd_handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Property escc_properties[] = {
|
static Property escc_properties[] = {
|
||||||
|
@ -1032,10 +1040,9 @@ static Property escc_properties[] = {
|
||||||
static void escc_class_init(ObjectClass *klass, void *data)
|
static void escc_class_init(ObjectClass *klass, void *data)
|
||||||
{
|
{
|
||||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
|
|
||||||
|
|
||||||
k->init = escc_init1;
|
|
||||||
dc->reset = escc_reset;
|
dc->reset = escc_reset;
|
||||||
|
dc->realize = escc_realize;
|
||||||
dc->vmsd = &vmstate_escc;
|
dc->vmsd = &vmstate_escc;
|
||||||
dc->props = escc_properties;
|
dc->props = escc_properties;
|
||||||
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
|
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
|
||||||
|
@ -1045,6 +1052,7 @@ static const TypeInfo escc_info = {
|
||||||
.name = TYPE_ESCC,
|
.name = TYPE_ESCC,
|
||||||
.parent = TYPE_SYS_BUS_DEVICE,
|
.parent = TYPE_SYS_BUS_DEVICE,
|
||||||
.instance_size = sizeof(ESCCState),
|
.instance_size = sizeof(ESCCState),
|
||||||
|
.instance_init = escc_init1,
|
||||||
.class_init = escc_class_init,
|
.class_init = escc_class_init,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -159,6 +159,11 @@ static const MemoryRegionOps ser_ops = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static Property etraxfs_ser_properties[] = {
|
||||||
|
DEFINE_PROP_CHR("chardev", ETRAXSerial, chr),
|
||||||
|
DEFINE_PROP_END_OF_LIST(),
|
||||||
|
};
|
||||||
|
|
||||||
static void serial_receive(void *opaque, const uint8_t *buf, int size)
|
static void serial_receive(void *opaque, const uint8_t *buf, int size)
|
||||||
{
|
{
|
||||||
ETRAXSerial *s = opaque;
|
ETRAXSerial *s = opaque;
|
||||||
|
@ -209,40 +214,42 @@ static void etraxfs_ser_reset(DeviceState *d)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int etraxfs_ser_init(SysBusDevice *dev)
|
static void etraxfs_ser_init(Object *obj)
|
||||||
|
{
|
||||||
|
ETRAXSerial *s = ETRAX_SERIAL(obj);
|
||||||
|
SysBusDevice *dev = SYS_BUS_DEVICE(obj);
|
||||||
|
|
||||||
|
sysbus_init_irq(dev, &s->irq);
|
||||||
|
memory_region_init_io(&s->mmio, obj, &ser_ops, s,
|
||||||
|
"etraxfs-serial", R_MAX * 4);
|
||||||
|
sysbus_init_mmio(dev, &s->mmio);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void etraxfs_ser_realize(DeviceState *dev, Error **errp)
|
||||||
{
|
{
|
||||||
ETRAXSerial *s = ETRAX_SERIAL(dev);
|
ETRAXSerial *s = ETRAX_SERIAL(dev);
|
||||||
|
|
||||||
sysbus_init_irq(dev, &s->irq);
|
|
||||||
memory_region_init_io(&s->mmio, OBJECT(s), &ser_ops, s,
|
|
||||||
"etraxfs-serial", R_MAX * 4);
|
|
||||||
sysbus_init_mmio(dev, &s->mmio);
|
|
||||||
|
|
||||||
/* FIXME use a qdev chardev prop instead of qemu_char_get_next_serial() */
|
|
||||||
s->chr = qemu_char_get_next_serial();
|
|
||||||
if (s->chr) {
|
if (s->chr) {
|
||||||
qemu_chr_add_handlers(s->chr,
|
qemu_chr_add_handlers(s->chr,
|
||||||
serial_can_receive, serial_receive,
|
serial_can_receive, serial_receive,
|
||||||
serial_event, s);
|
serial_event, s);
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void etraxfs_ser_class_init(ObjectClass *klass, void *data)
|
static void etraxfs_ser_class_init(ObjectClass *klass, void *data)
|
||||||
{
|
{
|
||||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
|
|
||||||
|
|
||||||
k->init = etraxfs_ser_init;
|
|
||||||
dc->reset = etraxfs_ser_reset;
|
dc->reset = etraxfs_ser_reset;
|
||||||
/* Reason: init() method uses qemu_char_get_next_serial() */
|
dc->props = etraxfs_ser_properties;
|
||||||
dc->cannot_instantiate_with_device_add_yet = true;
|
dc->realize = etraxfs_ser_realize;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo etraxfs_ser_info = {
|
static const TypeInfo etraxfs_ser_info = {
|
||||||
.name = TYPE_ETRAX_FS_SERIAL,
|
.name = TYPE_ETRAX_FS_SERIAL,
|
||||||
.parent = TYPE_SYS_BUS_DEVICE,
|
.parent = TYPE_SYS_BUS_DEVICE,
|
||||||
.instance_size = sizeof(ETRAXSerial),
|
.instance_size = sizeof(ETRAXSerial),
|
||||||
|
.instance_init = etraxfs_ser_init,
|
||||||
.class_init = etraxfs_ser_class_init,
|
.class_init = etraxfs_ser_class_init,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -114,17 +114,13 @@ static void juart_reset(DeviceState *d)
|
||||||
s->jrx = 0;
|
s->jrx = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int lm32_juart_init(SysBusDevice *dev)
|
static void lm32_juart_realize(DeviceState *dev, Error **errp)
|
||||||
{
|
{
|
||||||
LM32JuartState *s = LM32_JUART(dev);
|
LM32JuartState *s = LM32_JUART(dev);
|
||||||
|
|
||||||
/* FIXME use a qdev chardev prop instead of qemu_char_get_next_serial() */
|
|
||||||
s->chr = qemu_char_get_next_serial();
|
|
||||||
if (s->chr) {
|
if (s->chr) {
|
||||||
qemu_chr_add_handlers(s->chr, juart_can_rx, juart_rx, juart_event, s);
|
qemu_chr_add_handlers(s->chr, juart_can_rx, juart_rx, juart_event, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const VMStateDescription vmstate_lm32_juart = {
|
static const VMStateDescription vmstate_lm32_juart = {
|
||||||
|
@ -138,16 +134,19 @@ static const VMStateDescription vmstate_lm32_juart = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static Property lm32_juart_properties[] = {
|
||||||
|
DEFINE_PROP_CHR("chardev", LM32JuartState, chr),
|
||||||
|
DEFINE_PROP_END_OF_LIST(),
|
||||||
|
};
|
||||||
|
|
||||||
static void lm32_juart_class_init(ObjectClass *klass, void *data)
|
static void lm32_juart_class_init(ObjectClass *klass, void *data)
|
||||||
{
|
{
|
||||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
|
|
||||||
|
|
||||||
k->init = lm32_juart_init;
|
|
||||||
dc->reset = juart_reset;
|
dc->reset = juart_reset;
|
||||||
dc->vmsd = &vmstate_lm32_juart;
|
dc->vmsd = &vmstate_lm32_juart;
|
||||||
/* Reason: init() method uses qemu_char_get_next_serial() */
|
dc->props = lm32_juart_properties;
|
||||||
dc->cannot_instantiate_with_device_add_yet = true;
|
dc->realize = lm32_juart_realize;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo lm32_juart_info = {
|
static const TypeInfo lm32_juart_info = {
|
||||||
|
|
|
@ -249,23 +249,25 @@ static void uart_reset(DeviceState *d)
|
||||||
s->regs[R_LSR] = LSR_THRE | LSR_TEMT;
|
s->regs[R_LSR] = LSR_THRE | LSR_TEMT;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int lm32_uart_init(SysBusDevice *dev)
|
static void lm32_uart_init(Object *obj)
|
||||||
{
|
{
|
||||||
LM32UartState *s = LM32_UART(dev);
|
LM32UartState *s = LM32_UART(obj);
|
||||||
|
SysBusDevice *dev = SYS_BUS_DEVICE(obj);
|
||||||
|
|
||||||
sysbus_init_irq(dev, &s->irq);
|
sysbus_init_irq(dev, &s->irq);
|
||||||
|
|
||||||
memory_region_init_io(&s->iomem, OBJECT(s), &uart_ops, s,
|
memory_region_init_io(&s->iomem, obj, &uart_ops, s,
|
||||||
"uart", R_MAX * 4);
|
"uart", R_MAX * 4);
|
||||||
sysbus_init_mmio(dev, &s->iomem);
|
sysbus_init_mmio(dev, &s->iomem);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lm32_uart_realize(DeviceState *dev, Error **errp)
|
||||||
|
{
|
||||||
|
LM32UartState *s = LM32_UART(dev);
|
||||||
|
|
||||||
/* FIXME use a qdev chardev prop instead of qemu_char_get_next_serial() */
|
|
||||||
s->chr = qemu_char_get_next_serial();
|
|
||||||
if (s->chr) {
|
if (s->chr) {
|
||||||
qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s);
|
qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const VMStateDescription vmstate_lm32_uart = {
|
static const VMStateDescription vmstate_lm32_uart = {
|
||||||
|
@ -278,22 +280,26 @@ static const VMStateDescription vmstate_lm32_uart = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static Property lm32_uart_properties[] = {
|
||||||
|
DEFINE_PROP_CHR("chardev", LM32UartState, chr),
|
||||||
|
DEFINE_PROP_END_OF_LIST(),
|
||||||
|
};
|
||||||
|
|
||||||
static void lm32_uart_class_init(ObjectClass *klass, void *data)
|
static void lm32_uart_class_init(ObjectClass *klass, void *data)
|
||||||
{
|
{
|
||||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
|
|
||||||
|
|
||||||
k->init = lm32_uart_init;
|
|
||||||
dc->reset = uart_reset;
|
dc->reset = uart_reset;
|
||||||
dc->vmsd = &vmstate_lm32_uart;
|
dc->vmsd = &vmstate_lm32_uart;
|
||||||
/* Reason: init() method uses qemu_char_get_next_serial() */
|
dc->props = lm32_uart_properties;
|
||||||
dc->cannot_instantiate_with_device_add_yet = true;
|
dc->realize = lm32_uart_realize;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo lm32_uart_info = {
|
static const TypeInfo lm32_uart_info = {
|
||||||
.name = TYPE_LM32_UART,
|
.name = TYPE_LM32_UART,
|
||||||
.parent = TYPE_SYS_BUS_DEVICE,
|
.parent = TYPE_SYS_BUS_DEVICE,
|
||||||
.instance_size = sizeof(LM32UartState),
|
.instance_size = sizeof(LM32UartState),
|
||||||
|
.instance_init = lm32_uart_init,
|
||||||
.class_init = lm32_uart_class_init,
|
.class_init = lm32_uart_class_init,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -200,8 +200,6 @@ static void milkymist_uart_realize(DeviceState *dev, Error **errp)
|
||||||
{
|
{
|
||||||
MilkymistUartState *s = MILKYMIST_UART(dev);
|
MilkymistUartState *s = MILKYMIST_UART(dev);
|
||||||
|
|
||||||
/* FIXME use a qdev chardev prop instead of qemu_char_get_next_serial() */
|
|
||||||
s->chr = qemu_char_get_next_serial();
|
|
||||||
if (s->chr) {
|
if (s->chr) {
|
||||||
qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s);
|
qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s);
|
||||||
}
|
}
|
||||||
|
@ -229,6 +227,11 @@ static const VMStateDescription vmstate_milkymist_uart = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static Property milkymist_uart_properties[] = {
|
||||||
|
DEFINE_PROP_CHR("chardev", MilkymistUartState, chr),
|
||||||
|
DEFINE_PROP_END_OF_LIST(),
|
||||||
|
};
|
||||||
|
|
||||||
static void milkymist_uart_class_init(ObjectClass *klass, void *data)
|
static void milkymist_uart_class_init(ObjectClass *klass, void *data)
|
||||||
{
|
{
|
||||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
|
@ -236,8 +239,7 @@ static void milkymist_uart_class_init(ObjectClass *klass, void *data)
|
||||||
dc->realize = milkymist_uart_realize;
|
dc->realize = milkymist_uart_realize;
|
||||||
dc->reset = milkymist_uart_reset;
|
dc->reset = milkymist_uart_reset;
|
||||||
dc->vmsd = &vmstate_milkymist_uart;
|
dc->vmsd = &vmstate_milkymist_uart;
|
||||||
/* Reason: realize() method uses qemu_char_get_next_serial() */
|
dc->props = milkymist_uart_properties;
|
||||||
dc->cannot_instantiate_with_device_add_yet = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo milkymist_uart_info = {
|
static const TypeInfo milkymist_uart_info = {
|
||||||
|
|
|
@ -37,6 +37,7 @@
|
||||||
#include "sysemu/block-backend.h"
|
#include "sysemu/block-backend.h"
|
||||||
#include "exec/address-spaces.h"
|
#include "exec/address-spaces.h"
|
||||||
#include "sysemu/qtest.h"
|
#include "sysemu/qtest.h"
|
||||||
|
#include "sysemu/sysemu.h"
|
||||||
|
|
||||||
#define D(x)
|
#define D(x)
|
||||||
#define DNAND(x)
|
#define DNAND(x)
|
||||||
|
@ -341,8 +342,7 @@ void axisdev88_init(MachineState *machine)
|
||||||
sysbus_create_varargs("etraxfs,timer", 0x3005e000, irq[0x1b], nmi[1], NULL);
|
sysbus_create_varargs("etraxfs,timer", 0x3005e000, irq[0x1b], nmi[1], NULL);
|
||||||
|
|
||||||
for (i = 0; i < 4; i++) {
|
for (i = 0; i < 4; i++) {
|
||||||
sysbus_create_simple("etraxfs,serial", 0x30026000 + i * 0x2000,
|
etraxfs_ser_create(0x30026000 + i * 0x2000, irq[0x14 + i], serial_hds[i]);
|
||||||
irq[0x14 + i]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (kernel_filename) {
|
if (kernel_filename) {
|
||||||
|
|
|
@ -16,14 +16,31 @@ static inline DeviceState *lm32_pic_init(qemu_irq cpu_irq)
|
||||||
return dev;
|
return dev;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline DeviceState *lm32_juart_init(void)
|
static inline DeviceState *lm32_juart_init(CharDriverState *chr)
|
||||||
{
|
{
|
||||||
DeviceState *dev;
|
DeviceState *dev;
|
||||||
|
|
||||||
dev = qdev_create(NULL, TYPE_LM32_JUART);
|
dev = qdev_create(NULL, TYPE_LM32_JUART);
|
||||||
|
qdev_prop_set_chr(dev, "chardev", chr);
|
||||||
qdev_init_nofail(dev);
|
qdev_init_nofail(dev);
|
||||||
|
|
||||||
return dev;
|
return dev;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline DeviceState *lm32_uart_create(hwaddr addr,
|
||||||
|
qemu_irq irq,
|
||||||
|
CharDriverState *chr)
|
||||||
|
{
|
||||||
|
DeviceState *dev;
|
||||||
|
SysBusDevice *s;
|
||||||
|
|
||||||
|
dev = qdev_create(NULL, "lm32-uart");
|
||||||
|
s = SYS_BUS_DEVICE(dev);
|
||||||
|
qdev_prop_set_chr(dev, "chardev", chr);
|
||||||
|
qdev_init_nofail(dev);
|
||||||
|
sysbus_mmio_map(s, 0, addr);
|
||||||
|
sysbus_connect_irq(s, 0, irq);
|
||||||
|
return dev;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
#include "lm32_hwsetup.h"
|
#include "lm32_hwsetup.h"
|
||||||
#include "lm32.h"
|
#include "lm32.h"
|
||||||
#include "exec/address-spaces.h"
|
#include "exec/address-spaces.h"
|
||||||
|
#include "sysemu/sysemu.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
LM32CPU *cpu;
|
LM32CPU *cpu;
|
||||||
|
@ -131,12 +132,12 @@ static void lm32_evr_init(MachineState *machine)
|
||||||
irq[i] = qdev_get_gpio_in(env->pic_state, i);
|
irq[i] = qdev_get_gpio_in(env->pic_state, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
sysbus_create_simple("lm32-uart", uart0_base, irq[uart0_irq]);
|
lm32_uart_create(uart0_base, irq[uart0_irq], serial_hds[0]);
|
||||||
sysbus_create_simple("lm32-timer", timer0_base, irq[timer0_irq]);
|
sysbus_create_simple("lm32-timer", timer0_base, irq[timer0_irq]);
|
||||||
sysbus_create_simple("lm32-timer", timer1_base, irq[timer1_irq]);
|
sysbus_create_simple("lm32-timer", timer1_base, irq[timer1_irq]);
|
||||||
|
|
||||||
/* make sure juart isn't the first chardev */
|
/* make sure juart isn't the first chardev */
|
||||||
env->juart_state = lm32_juart_init();
|
env->juart_state = lm32_juart_init(serial_hds[1]);
|
||||||
|
|
||||||
reset_info->bootstrap_pc = flash_base;
|
reset_info->bootstrap_pc = flash_base;
|
||||||
|
|
||||||
|
@ -232,13 +233,13 @@ static void lm32_uclinux_init(MachineState *machine)
|
||||||
irq[i] = qdev_get_gpio_in(env->pic_state, i);
|
irq[i] = qdev_get_gpio_in(env->pic_state, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
sysbus_create_simple("lm32-uart", uart0_base, irq[uart0_irq]);
|
lm32_uart_create(uart0_base, irq[uart0_irq], serial_hds[0]);
|
||||||
sysbus_create_simple("lm32-timer", timer0_base, irq[timer0_irq]);
|
sysbus_create_simple("lm32-timer", timer0_base, irq[timer0_irq]);
|
||||||
sysbus_create_simple("lm32-timer", timer1_base, irq[timer1_irq]);
|
sysbus_create_simple("lm32-timer", timer1_base, irq[timer1_irq]);
|
||||||
sysbus_create_simple("lm32-timer", timer2_base, irq[timer2_irq]);
|
sysbus_create_simple("lm32-timer", timer2_base, irq[timer2_irq]);
|
||||||
|
|
||||||
/* make sure juart isn't the first chardev */
|
/* make sure juart isn't the first chardev */
|
||||||
env->juart_state = lm32_juart_init();
|
env->juart_state = lm32_juart_init(serial_hds[1]);
|
||||||
|
|
||||||
reset_info->bootstrap_pc = flash_base;
|
reset_info->bootstrap_pc = flash_base;
|
||||||
|
|
||||||
|
|
|
@ -5,11 +5,13 @@
|
||||||
#include "net/net.h"
|
#include "net/net.h"
|
||||||
|
|
||||||
static inline DeviceState *milkymist_uart_create(hwaddr base,
|
static inline DeviceState *milkymist_uart_create(hwaddr base,
|
||||||
qemu_irq irq)
|
qemu_irq irq,
|
||||||
|
CharDriverState *chr)
|
||||||
{
|
{
|
||||||
DeviceState *dev;
|
DeviceState *dev;
|
||||||
|
|
||||||
dev = qdev_create(NULL, "milkymist-uart");
|
dev = qdev_create(NULL, "milkymist-uart");
|
||||||
|
qdev_prop_set_chr(dev, "chardev", chr);
|
||||||
qdev_init_nofail(dev);
|
qdev_init_nofail(dev);
|
||||||
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base);
|
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base);
|
||||||
sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq);
|
sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq);
|
||||||
|
|
|
@ -159,7 +159,7 @@ milkymist_init(MachineState *machine)
|
||||||
}
|
}
|
||||||
g_free(bios_filename);
|
g_free(bios_filename);
|
||||||
|
|
||||||
milkymist_uart_create(0x60000000, irq[0]);
|
milkymist_uart_create(0x60000000, irq[0], serial_hds[0]);
|
||||||
milkymist_sysctl_create(0x60001000, irq[1], irq[2], irq[3],
|
milkymist_sysctl_create(0x60001000, irq[1], irq[2], irq[3],
|
||||||
80000000, 0x10014d31, 0x0000041f, 0x00000001);
|
80000000, 0x10014d31, 0x0000041f, 0x00000001);
|
||||||
milkymist_hpdmc_create(0x60002000);
|
milkymist_hpdmc_create(0x60002000);
|
||||||
|
@ -175,7 +175,7 @@ milkymist_init(MachineState *machine)
|
||||||
0x20000000, 0x1000, 0x20020000, 0x2000);
|
0x20000000, 0x1000, 0x20020000, 0x2000);
|
||||||
|
|
||||||
/* make sure juart isn't the first chardev */
|
/* make sure juart isn't the first chardev */
|
||||||
env->juart_state = lm32_juart_init();
|
env->juart_state = lm32_juart_init(serial_hds[1]);
|
||||||
|
|
||||||
if (kernel_filename) {
|
if (kernel_filename) {
|
||||||
uint64_t entry;
|
uint64_t entry;
|
||||||
|
|
|
@ -33,7 +33,6 @@
|
||||||
#include "sysemu/hostmem.h"
|
#include "sysemu/hostmem.h"
|
||||||
#include "sysemu/qtest.h"
|
#include "sysemu/qtest.h"
|
||||||
#include "qapi/visitor.h"
|
#include "qapi/visitor.h"
|
||||||
#include "exec/ram_addr.h"
|
|
||||||
|
|
||||||
#include "hw/misc/ivshmem.h"
|
#include "hw/misc/ivshmem.h"
|
||||||
|
|
||||||
|
@ -533,7 +532,7 @@ static void process_msg_shmem(IVShmemState *s, int fd, Error **errp)
|
||||||
}
|
}
|
||||||
memory_region_init_ram_ptr(&s->server_bar2, OBJECT(s),
|
memory_region_init_ram_ptr(&s->server_bar2, OBJECT(s),
|
||||||
"ivshmem.bar2", size, ptr);
|
"ivshmem.bar2", size, ptr);
|
||||||
qemu_set_ram_fd(memory_region_get_ram_addr(&s->server_bar2), fd);
|
memory_region_set_fd(&s->server_bar2, fd);
|
||||||
s->ivshmem_bar2 = &s->server_bar2;
|
s->ivshmem_bar2 = &s->server_bar2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -940,7 +939,7 @@ static void ivshmem_exit(PCIDevice *dev)
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
}
|
}
|
||||||
|
|
||||||
fd = qemu_get_ram_fd(memory_region_get_ram_addr(s->ivshmem_bar2));
|
fd = memory_region_get_fd(s->ivshmem_bar2);
|
||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -650,7 +650,9 @@ static int megasas_init_firmware(MegasasState *s, MegasasCmd *cmd)
|
||||||
pa_hi = le32_to_cpu(initq->pi_addr_hi);
|
pa_hi = le32_to_cpu(initq->pi_addr_hi);
|
||||||
s->producer_pa = ((uint64_t) pa_hi << 32) | pa_lo;
|
s->producer_pa = ((uint64_t) pa_hi << 32) | pa_lo;
|
||||||
s->reply_queue_head = ldl_le_pci_dma(pcid, s->producer_pa);
|
s->reply_queue_head = ldl_le_pci_dma(pcid, s->producer_pa);
|
||||||
|
s->reply_queue_head %= MEGASAS_MAX_FRAMES;
|
||||||
s->reply_queue_tail = ldl_le_pci_dma(pcid, s->consumer_pa);
|
s->reply_queue_tail = ldl_le_pci_dma(pcid, s->consumer_pa);
|
||||||
|
s->reply_queue_tail %= MEGASAS_MAX_FRAMES;
|
||||||
flags = le32_to_cpu(initq->flags);
|
flags = le32_to_cpu(initq->flags);
|
||||||
if (flags & MFI_QUEUE_FLAG_CONTEXT64) {
|
if (flags & MFI_QUEUE_FLAG_CONTEXT64) {
|
||||||
s->flags |= MEGASAS_MASK_USE_QUEUE64;
|
s->flags |= MEGASAS_MASK_USE_QUEUE64;
|
||||||
|
@ -1293,7 +1295,7 @@ static int megasas_dcmd_ld_get_info(MegasasState *s, MegasasCmd *cmd)
|
||||||
|
|
||||||
static int megasas_dcmd_cfg_read(MegasasState *s, MegasasCmd *cmd)
|
static int megasas_dcmd_cfg_read(MegasasState *s, MegasasCmd *cmd)
|
||||||
{
|
{
|
||||||
uint8_t data[4096];
|
uint8_t data[4096] = { 0 };
|
||||||
struct mfi_config_data *info;
|
struct mfi_config_data *info;
|
||||||
int num_pd_disks = 0, array_offset, ld_offset;
|
int num_pd_disks = 0, array_offset, ld_offset;
|
||||||
BusChild *kid;
|
BusChild *kid;
|
||||||
|
@ -1446,7 +1448,7 @@ static int megasas_dcmd_set_properties(MegasasState *s, MegasasCmd *cmd)
|
||||||
dcmd_size);
|
dcmd_size);
|
||||||
return MFI_STAT_INVALID_PARAMETER;
|
return MFI_STAT_INVALID_PARAMETER;
|
||||||
}
|
}
|
||||||
dma_buf_write((uint8_t *)&info, cmd->iov_size, &cmd->qsg);
|
dma_buf_write((uint8_t *)&info, dcmd_size, &cmd->qsg);
|
||||||
trace_megasas_dcmd_unsupported(cmd->index, cmd->iov_size);
|
trace_megasas_dcmd_unsupported(cmd->index, cmd->iov_size);
|
||||||
return MFI_STAT_OK;
|
return MFI_STAT_OK;
|
||||||
}
|
}
|
||||||
|
|
|
@ -754,11 +754,6 @@ static void mptsas_fetch_request(MPTSASState *s)
|
||||||
hwaddr addr;
|
hwaddr addr;
|
||||||
int size;
|
int size;
|
||||||
|
|
||||||
if (s->state != MPI_IOC_STATE_OPERATIONAL) {
|
|
||||||
mptsas_set_fault(s, MPI_IOCSTATUS_INVALID_STATE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Read the message header from the guest first. */
|
/* Read the message header from the guest first. */
|
||||||
addr = s->host_mfa_high_addr | MPTSAS_FIFO_GET(s, request_post);
|
addr = s->host_mfa_high_addr | MPTSAS_FIFO_GET(s, request_post);
|
||||||
pci_dma_read(pci, addr, req, sizeof(hdr));
|
pci_dma_read(pci, addr, req, sizeof(hdr));
|
||||||
|
@ -789,6 +784,10 @@ static void mptsas_fetch_requests(void *opaque)
|
||||||
{
|
{
|
||||||
MPTSASState *s = opaque;
|
MPTSASState *s = opaque;
|
||||||
|
|
||||||
|
if (s->state != MPI_IOC_STATE_OPERATIONAL) {
|
||||||
|
mptsas_set_fault(s, MPI_IOCSTATUS_INVALID_STATE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
while (!MPTSAS_FIFO_EMPTY(s, request_post)) {
|
while (!MPTSAS_FIFO_EMPTY(s, request_post)) {
|
||||||
mptsas_fetch_request(s);
|
mptsas_fetch_request(s);
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,21 @@ do { printf("scsi-disk: " fmt , ## __VA_ARGS__); } while (0)
|
||||||
#define DEFAULT_MAX_UNMAP_SIZE (1 << 30) /* 1 GB */
|
#define DEFAULT_MAX_UNMAP_SIZE (1 << 30) /* 1 GB */
|
||||||
#define DEFAULT_MAX_IO_SIZE INT_MAX /* 2 GB - 1 block */
|
#define DEFAULT_MAX_IO_SIZE INT_MAX /* 2 GB - 1 block */
|
||||||
|
|
||||||
typedef struct SCSIDiskState SCSIDiskState;
|
#define TYPE_SCSI_DISK_BASE "scsi-disk-base"
|
||||||
|
|
||||||
|
#define SCSI_DISK_BASE(obj) \
|
||||||
|
OBJECT_CHECK(SCSIDiskState, (obj), TYPE_SCSI_DISK_BASE)
|
||||||
|
#define SCSI_DISK_BASE_CLASS(klass) \
|
||||||
|
OBJECT_CLASS_CHECK(SCSIDiskClass, (klass), TYPE_SCSI_DISK_BASE)
|
||||||
|
#define SCSI_DISK_BASE_GET_CLASS(obj) \
|
||||||
|
OBJECT_GET_CLASS(SCSIDiskClass, (obj), TYPE_SCSI_DISK_BASE)
|
||||||
|
|
||||||
|
typedef struct SCSIDiskClass {
|
||||||
|
SCSIDeviceClass parent_class;
|
||||||
|
DMAIOFunc *dma_readv;
|
||||||
|
DMAIOFunc *dma_writev;
|
||||||
|
bool (*need_fua_emulation)(SCSICommand *cmd);
|
||||||
|
} SCSIDiskClass;
|
||||||
|
|
||||||
typedef struct SCSIDiskReq {
|
typedef struct SCSIDiskReq {
|
||||||
SCSIRequest req;
|
SCSIRequest req;
|
||||||
|
@ -62,16 +76,18 @@ typedef struct SCSIDiskReq {
|
||||||
uint32_t sector_count;
|
uint32_t sector_count;
|
||||||
uint32_t buflen;
|
uint32_t buflen;
|
||||||
bool started;
|
bool started;
|
||||||
|
bool need_fua_emulation;
|
||||||
struct iovec iov;
|
struct iovec iov;
|
||||||
QEMUIOVector qiov;
|
QEMUIOVector qiov;
|
||||||
BlockAcctCookie acct;
|
BlockAcctCookie acct;
|
||||||
|
unsigned char *status;
|
||||||
} SCSIDiskReq;
|
} SCSIDiskReq;
|
||||||
|
|
||||||
#define SCSI_DISK_F_REMOVABLE 0
|
#define SCSI_DISK_F_REMOVABLE 0
|
||||||
#define SCSI_DISK_F_DPOFUA 1
|
#define SCSI_DISK_F_DPOFUA 1
|
||||||
#define SCSI_DISK_F_NO_REMOVABLE_DEVOPS 2
|
#define SCSI_DISK_F_NO_REMOVABLE_DEVOPS 2
|
||||||
|
|
||||||
struct SCSIDiskState
|
typedef struct SCSIDiskState
|
||||||
{
|
{
|
||||||
SCSIDevice qdev;
|
SCSIDevice qdev;
|
||||||
uint32_t features;
|
uint32_t features;
|
||||||
|
@ -88,7 +104,7 @@ struct SCSIDiskState
|
||||||
char *product;
|
char *product;
|
||||||
bool tray_open;
|
bool tray_open;
|
||||||
bool tray_locked;
|
bool tray_locked;
|
||||||
};
|
} SCSIDiskState;
|
||||||
|
|
||||||
static int scsi_handle_rw_error(SCSIDiskReq *r, int error, bool acct_failed);
|
static int scsi_handle_rw_error(SCSIDiskReq *r, int error, bool acct_failed);
|
||||||
|
|
||||||
|
@ -161,6 +177,29 @@ static void scsi_disk_load_request(QEMUFile *f, SCSIRequest *req)
|
||||||
qemu_iovec_init_external(&r->qiov, &r->iov, 1);
|
qemu_iovec_init_external(&r->qiov, &r->iov, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool scsi_disk_req_check_error(SCSIDiskReq *r, int ret, bool acct_failed)
|
||||||
|
{
|
||||||
|
if (r->req.io_canceled) {
|
||||||
|
scsi_req_cancel_complete(&r->req);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
return scsi_handle_rw_error(r, -ret, acct_failed);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r->status && *r->status) {
|
||||||
|
if (acct_failed) {
|
||||||
|
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
|
||||||
|
block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct);
|
||||||
|
}
|
||||||
|
scsi_req_complete(&r->req, *r->status);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static void scsi_aio_complete(void *opaque, int ret)
|
static void scsi_aio_complete(void *opaque, int ret)
|
||||||
{
|
{
|
||||||
SCSIDiskReq *r = (SCSIDiskReq *)opaque;
|
SCSIDiskReq *r = (SCSIDiskReq *)opaque;
|
||||||
|
@ -168,17 +207,10 @@ static void scsi_aio_complete(void *opaque, int ret)
|
||||||
|
|
||||||
assert(r->req.aiocb != NULL);
|
assert(r->req.aiocb != NULL);
|
||||||
r->req.aiocb = NULL;
|
r->req.aiocb = NULL;
|
||||||
if (r->req.io_canceled) {
|
if (scsi_disk_req_check_error(r, ret, true)) {
|
||||||
scsi_req_cancel_complete(&r->req);
|
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret < 0) {
|
|
||||||
if (scsi_handle_rw_error(r, -ret, true)) {
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
|
block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
|
||||||
scsi_req_complete(&r->req, GOOD);
|
scsi_req_complete(&r->req, GOOD);
|
||||||
|
|
||||||
|
@ -217,13 +249,9 @@ static void scsi_write_do_fua(SCSIDiskReq *r)
|
||||||
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
|
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
|
||||||
|
|
||||||
assert(r->req.aiocb == NULL);
|
assert(r->req.aiocb == NULL);
|
||||||
|
assert(!r->req.io_canceled);
|
||||||
|
|
||||||
if (r->req.io_canceled) {
|
if (r->need_fua_emulation) {
|
||||||
scsi_req_cancel_complete(&r->req);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (scsi_is_cmd_fua(&r->req.cmd)) {
|
|
||||||
block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct, 0,
|
block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct, 0,
|
||||||
BLOCK_ACCT_FLUSH);
|
BLOCK_ACCT_FLUSH);
|
||||||
r->req.aiocb = blk_aio_flush(s->qdev.conf.blk, scsi_aio_complete, r);
|
r->req.aiocb = blk_aio_flush(s->qdev.conf.blk, scsi_aio_complete, r);
|
||||||
|
@ -231,26 +259,16 @@ static void scsi_write_do_fua(SCSIDiskReq *r)
|
||||||
}
|
}
|
||||||
|
|
||||||
scsi_req_complete(&r->req, GOOD);
|
scsi_req_complete(&r->req, GOOD);
|
||||||
|
|
||||||
done:
|
|
||||||
scsi_req_unref(&r->req);
|
scsi_req_unref(&r->req);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void scsi_dma_complete_noio(SCSIDiskReq *r, int ret)
|
static void scsi_dma_complete_noio(SCSIDiskReq *r, int ret)
|
||||||
{
|
{
|
||||||
assert(r->req.aiocb == NULL);
|
assert(r->req.aiocb == NULL);
|
||||||
|
if (scsi_disk_req_check_error(r, ret, false)) {
|
||||||
if (r->req.io_canceled) {
|
|
||||||
scsi_req_cancel_complete(&r->req);
|
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret < 0) {
|
|
||||||
if (scsi_handle_rw_error(r, -ret, false)) {
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
r->sector += r->sector_count;
|
r->sector += r->sector_count;
|
||||||
r->sector_count = 0;
|
r->sector_count = 0;
|
||||||
if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
|
if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
|
||||||
|
@ -288,17 +306,10 @@ static void scsi_read_complete(void * opaque, int ret)
|
||||||
|
|
||||||
assert(r->req.aiocb != NULL);
|
assert(r->req.aiocb != NULL);
|
||||||
r->req.aiocb = NULL;
|
r->req.aiocb = NULL;
|
||||||
if (r->req.io_canceled) {
|
if (scsi_disk_req_check_error(r, ret, true)) {
|
||||||
scsi_req_cancel_complete(&r->req);
|
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret < 0) {
|
|
||||||
if (scsi_handle_rw_error(r, -ret, true)) {
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
|
block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
|
||||||
DPRINTF("Data ready tag=0x%x len=%zd\n", r->req.tag, r->qiov.size);
|
DPRINTF("Data ready tag=0x%x len=%zd\n", r->req.tag, r->qiov.size);
|
||||||
|
|
||||||
|
@ -315,36 +326,29 @@ done:
|
||||||
static void scsi_do_read(SCSIDiskReq *r, int ret)
|
static void scsi_do_read(SCSIDiskReq *r, int ret)
|
||||||
{
|
{
|
||||||
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
|
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
|
||||||
|
SCSIDiskClass *sdc = (SCSIDiskClass *) object_get_class(OBJECT(s));
|
||||||
|
|
||||||
assert (r->req.aiocb == NULL);
|
assert (r->req.aiocb == NULL);
|
||||||
|
if (scsi_disk_req_check_error(r, ret, false)) {
|
||||||
if (r->req.io_canceled) {
|
|
||||||
scsi_req_cancel_complete(&r->req);
|
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret < 0) {
|
|
||||||
if (scsi_handle_rw_error(r, -ret, false)) {
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The request is used as the AIO opaque value, so add a ref. */
|
/* The request is used as the AIO opaque value, so add a ref. */
|
||||||
scsi_req_ref(&r->req);
|
scsi_req_ref(&r->req);
|
||||||
|
|
||||||
if (r->req.sg) {
|
if (r->req.sg) {
|
||||||
dma_acct_start(s->qdev.conf.blk, &r->acct, r->req.sg, BLOCK_ACCT_READ);
|
dma_acct_start(s->qdev.conf.blk, &r->acct, r->req.sg, BLOCK_ACCT_READ);
|
||||||
r->req.resid -= r->req.sg->size;
|
r->req.resid -= r->req.sg->size;
|
||||||
r->req.aiocb = dma_blk_read(s->qdev.conf.blk, r->req.sg,
|
r->req.aiocb = dma_blk_io(blk_get_aio_context(s->qdev.conf.blk),
|
||||||
r->sector << BDRV_SECTOR_BITS,
|
r->req.sg, r->sector << BDRV_SECTOR_BITS,
|
||||||
scsi_dma_complete, r);
|
sdc->dma_readv, r, scsi_dma_complete, r,
|
||||||
|
DMA_DIRECTION_FROM_DEVICE);
|
||||||
} else {
|
} else {
|
||||||
scsi_init_iovec(r, SCSI_DMA_BUF_SIZE);
|
scsi_init_iovec(r, SCSI_DMA_BUF_SIZE);
|
||||||
block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct,
|
block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct,
|
||||||
r->qiov.size, BLOCK_ACCT_READ);
|
r->qiov.size, BLOCK_ACCT_READ);
|
||||||
r->req.aiocb = blk_aio_preadv(s->qdev.conf.blk,
|
r->req.aiocb = sdc->dma_readv(r->sector, &r->qiov,
|
||||||
r->sector << BDRV_SECTOR_BITS, &r->qiov,
|
scsi_read_complete, r, r);
|
||||||
0, scsi_read_complete, r);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
|
@ -399,7 +403,7 @@ static void scsi_read_data(SCSIRequest *req)
|
||||||
|
|
||||||
first = !r->started;
|
first = !r->started;
|
||||||
r->started = true;
|
r->started = true;
|
||||||
if (first && scsi_is_cmd_fua(&r->req.cmd)) {
|
if (first && r->need_fua_emulation) {
|
||||||
block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct, 0,
|
block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct, 0,
|
||||||
BLOCK_ACCT_FLUSH);
|
BLOCK_ACCT_FLUSH);
|
||||||
r->req.aiocb = blk_aio_flush(s->qdev.conf.blk, scsi_do_read_cb, r);
|
r->req.aiocb = blk_aio_flush(s->qdev.conf.blk, scsi_do_read_cb, r);
|
||||||
|
@ -456,18 +460,10 @@ static void scsi_write_complete_noio(SCSIDiskReq *r, int ret)
|
||||||
uint32_t n;
|
uint32_t n;
|
||||||
|
|
||||||
assert (r->req.aiocb == NULL);
|
assert (r->req.aiocb == NULL);
|
||||||
|
if (scsi_disk_req_check_error(r, ret, false)) {
|
||||||
if (r->req.io_canceled) {
|
|
||||||
scsi_req_cancel_complete(&r->req);
|
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret < 0) {
|
|
||||||
if (scsi_handle_rw_error(r, -ret, false)) {
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
n = r->qiov.size / 512;
|
n = r->qiov.size / 512;
|
||||||
r->sector += n;
|
r->sector += n;
|
||||||
r->sector_count -= n;
|
r->sector_count -= n;
|
||||||
|
@ -504,6 +500,7 @@ static void scsi_write_data(SCSIRequest *req)
|
||||||
{
|
{
|
||||||
SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
|
SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
|
||||||
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
|
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
|
||||||
|
SCSIDiskClass *sdc = (SCSIDiskClass *) object_get_class(OBJECT(s));
|
||||||
|
|
||||||
/* No data transfer may already be in progress */
|
/* No data transfer may already be in progress */
|
||||||
assert(r->req.aiocb == NULL);
|
assert(r->req.aiocb == NULL);
|
||||||
|
@ -540,15 +537,15 @@ static void scsi_write_data(SCSIRequest *req)
|
||||||
if (r->req.sg) {
|
if (r->req.sg) {
|
||||||
dma_acct_start(s->qdev.conf.blk, &r->acct, r->req.sg, BLOCK_ACCT_WRITE);
|
dma_acct_start(s->qdev.conf.blk, &r->acct, r->req.sg, BLOCK_ACCT_WRITE);
|
||||||
r->req.resid -= r->req.sg->size;
|
r->req.resid -= r->req.sg->size;
|
||||||
r->req.aiocb = dma_blk_write(s->qdev.conf.blk, r->req.sg,
|
r->req.aiocb = dma_blk_io(blk_get_aio_context(s->qdev.conf.blk),
|
||||||
r->sector << BDRV_SECTOR_BITS,
|
r->req.sg, r->sector << BDRV_SECTOR_BITS,
|
||||||
scsi_dma_complete, r);
|
sdc->dma_writev, r, scsi_dma_complete, r,
|
||||||
|
DMA_DIRECTION_TO_DEVICE);
|
||||||
} else {
|
} else {
|
||||||
block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct,
|
block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct,
|
||||||
r->qiov.size, BLOCK_ACCT_WRITE);
|
r->qiov.size, BLOCK_ACCT_WRITE);
|
||||||
r->req.aiocb = blk_aio_pwritev(s->qdev.conf.blk,
|
r->req.aiocb = sdc->dma_writev(r->sector << BDRV_SECTOR_BITS, &r->qiov,
|
||||||
r->sector << BDRV_SECTOR_BITS, &r->qiov,
|
scsi_write_complete, r, r);
|
||||||
0, scsi_write_complete, r);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1600,18 +1597,10 @@ static void scsi_unmap_complete_noio(UnmapCBData *data, int ret)
|
||||||
uint32_t nb_sectors;
|
uint32_t nb_sectors;
|
||||||
|
|
||||||
assert(r->req.aiocb == NULL);
|
assert(r->req.aiocb == NULL);
|
||||||
|
if (scsi_disk_req_check_error(r, ret, false)) {
|
||||||
if (r->req.io_canceled) {
|
|
||||||
scsi_req_cancel_complete(&r->req);
|
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret < 0) {
|
|
||||||
if (scsi_handle_rw_error(r, -ret, false)) {
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data->count > 0) {
|
if (data->count > 0) {
|
||||||
sector_num = ldq_be_p(&data->inbuf[0]);
|
sector_num = ldq_be_p(&data->inbuf[0]);
|
||||||
nb_sectors = ldl_be_p(&data->inbuf[8]) & 0xffffffffULL;
|
nb_sectors = ldl_be_p(&data->inbuf[8]) & 0xffffffffULL;
|
||||||
|
@ -1711,17 +1700,10 @@ static void scsi_write_same_complete(void *opaque, int ret)
|
||||||
|
|
||||||
assert(r->req.aiocb != NULL);
|
assert(r->req.aiocb != NULL);
|
||||||
r->req.aiocb = NULL;
|
r->req.aiocb = NULL;
|
||||||
if (r->req.io_canceled) {
|
if (scsi_disk_req_check_error(r, ret, true)) {
|
||||||
scsi_req_cancel_complete(&r->req);
|
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret < 0) {
|
|
||||||
if (scsi_handle_rw_error(r, -ret, true)) {
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
|
block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
|
||||||
|
|
||||||
data->nb_sectors -= data->iov.iov_len / 512;
|
data->nb_sectors -= data->iov.iov_len / 512;
|
||||||
|
@ -2138,6 +2120,7 @@ static int32_t scsi_disk_dma_command(SCSIRequest *req, uint8_t *buf)
|
||||||
{
|
{
|
||||||
SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
|
SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
|
||||||
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
|
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
|
||||||
|
SCSIDiskClass *sdc = (SCSIDiskClass *) object_get_class(OBJECT(s));
|
||||||
uint32_t len;
|
uint32_t len;
|
||||||
uint8_t command;
|
uint8_t command;
|
||||||
|
|
||||||
|
@ -2196,6 +2179,7 @@ static int32_t scsi_disk_dma_command(SCSIRequest *req, uint8_t *buf)
|
||||||
scsi_check_condition(r, SENSE_CODE(LBA_OUT_OF_RANGE));
|
scsi_check_condition(r, SENSE_CODE(LBA_OUT_OF_RANGE));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
r->need_fua_emulation = sdc->need_fua_emulation(&r->req.cmd);
|
||||||
if (r->sector_count == 0) {
|
if (r->sector_count == 0) {
|
||||||
scsi_req_complete(&r->req, GOOD);
|
scsi_req_complete(&r->req, GOOD);
|
||||||
}
|
}
|
||||||
|
@ -2578,16 +2562,145 @@ static void scsi_block_realize(SCSIDevice *dev, Error **errp)
|
||||||
scsi_generic_read_device_identification(&s->qdev);
|
scsi_generic_read_device_identification(&s->qdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct SCSIBlockReq {
|
||||||
|
SCSIDiskReq req;
|
||||||
|
sg_io_hdr_t io_header;
|
||||||
|
|
||||||
|
/* Selected bytes of the original CDB, copied into our own CDB. */
|
||||||
|
uint8_t cmd, cdb1, group_number;
|
||||||
|
|
||||||
|
/* CDB passed to SG_IO. */
|
||||||
|
uint8_t cdb[16];
|
||||||
|
} SCSIBlockReq;
|
||||||
|
|
||||||
|
static BlockAIOCB *scsi_block_do_sgio(SCSIBlockReq *req,
|
||||||
|
int64_t offset, QEMUIOVector *iov,
|
||||||
|
int direction,
|
||||||
|
BlockCompletionFunc *cb, void *opaque)
|
||||||
|
{
|
||||||
|
sg_io_hdr_t *io_header = &req->io_header;
|
||||||
|
SCSIDiskReq *r = &req->req;
|
||||||
|
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
|
||||||
|
int nb_logical_blocks;
|
||||||
|
uint64_t lba;
|
||||||
|
BlockAIOCB *aiocb;
|
||||||
|
|
||||||
|
/* This is not supported yet. It can only happen if the guest does
|
||||||
|
* reads and writes that are not aligned to one logical sectors
|
||||||
|
* _and_ cover multiple MemoryRegions.
|
||||||
|
*/
|
||||||
|
assert(offset % s->qdev.blocksize == 0);
|
||||||
|
assert(iov->size % s->qdev.blocksize == 0);
|
||||||
|
|
||||||
|
io_header->interface_id = 'S';
|
||||||
|
|
||||||
|
/* The data transfer comes from the QEMUIOVector. */
|
||||||
|
io_header->dxfer_direction = direction;
|
||||||
|
io_header->dxfer_len = iov->size;
|
||||||
|
io_header->dxferp = (void *)iov->iov;
|
||||||
|
io_header->iovec_count = iov->niov;
|
||||||
|
assert(io_header->iovec_count == iov->niov); /* no overflow! */
|
||||||
|
|
||||||
|
/* Build a new CDB with the LBA and length patched in, in case
|
||||||
|
* DMA helpers split the transfer in multiple segments. Do not
|
||||||
|
* build a CDB smaller than what the guest wanted, and only build
|
||||||
|
* a larger one if strictly necessary.
|
||||||
|
*/
|
||||||
|
io_header->cmdp = req->cdb;
|
||||||
|
lba = offset / s->qdev.blocksize;
|
||||||
|
nb_logical_blocks = io_header->dxfer_len / s->qdev.blocksize;
|
||||||
|
|
||||||
|
if ((req->cmd >> 5) == 0 && lba <= 0x1ffff) {
|
||||||
|
/* 6-byte CDB */
|
||||||
|
stl_be_p(&req->cdb[0], lba | (req->cmd << 24));
|
||||||
|
req->cdb[4] = nb_logical_blocks;
|
||||||
|
req->cdb[5] = 0;
|
||||||
|
io_header->cmd_len = 6;
|
||||||
|
} else if ((req->cmd >> 5) <= 1 && lba <= 0xffffffffULL) {
|
||||||
|
/* 10-byte CDB */
|
||||||
|
req->cdb[0] = (req->cmd & 0x1f) | 0x20;
|
||||||
|
req->cdb[1] = req->cdb1;
|
||||||
|
stl_be_p(&req->cdb[2], lba);
|
||||||
|
req->cdb[6] = req->group_number;
|
||||||
|
stw_be_p(&req->cdb[7], nb_logical_blocks);
|
||||||
|
req->cdb[9] = 0;
|
||||||
|
io_header->cmd_len = 10;
|
||||||
|
} else if ((req->cmd >> 5) != 4 && lba <= 0xffffffffULL) {
|
||||||
|
/* 12-byte CDB */
|
||||||
|
req->cdb[0] = (req->cmd & 0x1f) | 0xA0;
|
||||||
|
req->cdb[1] = req->cdb1;
|
||||||
|
stl_be_p(&req->cdb[2], lba);
|
||||||
|
stl_be_p(&req->cdb[6], nb_logical_blocks);
|
||||||
|
req->cdb[10] = req->group_number;
|
||||||
|
req->cdb[11] = 0;
|
||||||
|
io_header->cmd_len = 12;
|
||||||
|
} else {
|
||||||
|
/* 16-byte CDB */
|
||||||
|
req->cdb[0] = (req->cmd & 0x1f) | 0x80;
|
||||||
|
req->cdb[1] = req->cdb1;
|
||||||
|
stq_be_p(&req->cdb[2], lba);
|
||||||
|
stl_be_p(&req->cdb[10], nb_logical_blocks);
|
||||||
|
req->cdb[14] = req->group_number;
|
||||||
|
req->cdb[15] = 0;
|
||||||
|
io_header->cmd_len = 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The rest is as in scsi-generic.c. */
|
||||||
|
io_header->mx_sb_len = sizeof(r->req.sense);
|
||||||
|
io_header->sbp = r->req.sense;
|
||||||
|
io_header->timeout = UINT_MAX;
|
||||||
|
io_header->usr_ptr = r;
|
||||||
|
io_header->flags |= SG_FLAG_DIRECT_IO;
|
||||||
|
|
||||||
|
aiocb = blk_aio_ioctl(s->qdev.conf.blk, SG_IO, io_header, cb, opaque);
|
||||||
|
assert(aiocb != NULL);
|
||||||
|
return aiocb;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool scsi_block_no_fua(SCSICommand *cmd)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BlockAIOCB *scsi_block_dma_readv(int64_t offset,
|
||||||
|
QEMUIOVector *iov,
|
||||||
|
BlockCompletionFunc *cb, void *cb_opaque,
|
||||||
|
void *opaque)
|
||||||
|
{
|
||||||
|
SCSIBlockReq *r = opaque;
|
||||||
|
return scsi_block_do_sgio(r, offset, iov,
|
||||||
|
SG_DXFER_FROM_DEV, cb, cb_opaque);
|
||||||
|
}
|
||||||
|
|
||||||
|
static BlockAIOCB *scsi_block_dma_writev(int64_t offset,
|
||||||
|
QEMUIOVector *iov,
|
||||||
|
BlockCompletionFunc *cb, void *cb_opaque,
|
||||||
|
void *opaque)
|
||||||
|
{
|
||||||
|
SCSIBlockReq *r = opaque;
|
||||||
|
return scsi_block_do_sgio(r, offset, iov,
|
||||||
|
SG_DXFER_TO_DEV, cb, cb_opaque);
|
||||||
|
}
|
||||||
|
|
||||||
static bool scsi_block_is_passthrough(SCSIDiskState *s, uint8_t *buf)
|
static bool scsi_block_is_passthrough(SCSIDiskState *s, uint8_t *buf)
|
||||||
{
|
{
|
||||||
switch (buf[0]) {
|
switch (buf[0]) {
|
||||||
|
case VERIFY_10:
|
||||||
|
case VERIFY_12:
|
||||||
|
case VERIFY_16:
|
||||||
|
/* Check if BYTCHK == 0x01 (data-out buffer contains data
|
||||||
|
* for the number of logical blocks specified in the length
|
||||||
|
* field). For other modes, do not use scatter/gather operation.
|
||||||
|
*/
|
||||||
|
if ((buf[1] & 6) != 2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case READ_6:
|
case READ_6:
|
||||||
case READ_10:
|
case READ_10:
|
||||||
case READ_12:
|
case READ_12:
|
||||||
case READ_16:
|
case READ_16:
|
||||||
case VERIFY_10:
|
|
||||||
case VERIFY_12:
|
|
||||||
case VERIFY_16:
|
|
||||||
case WRITE_6:
|
case WRITE_6:
|
||||||
case WRITE_10:
|
case WRITE_10:
|
||||||
case WRITE_12:
|
case WRITE_12:
|
||||||
|
@ -2595,21 +2708,8 @@ static bool scsi_block_is_passthrough(SCSIDiskState *s, uint8_t *buf)
|
||||||
case WRITE_VERIFY_10:
|
case WRITE_VERIFY_10:
|
||||||
case WRITE_VERIFY_12:
|
case WRITE_VERIFY_12:
|
||||||
case WRITE_VERIFY_16:
|
case WRITE_VERIFY_16:
|
||||||
/* If we are not using O_DIRECT, we might read stale data from the
|
/* MMC writing cannot be done via DMA helpers, because it sometimes
|
||||||
* host cache if writes were made using other commands than these
|
|
||||||
* ones (such as WRITE SAME or EXTENDED COPY, etc.). So, without
|
|
||||||
* O_DIRECT everything must go through SG_IO.
|
|
||||||
*/
|
|
||||||
if (!(blk_get_flags(s->qdev.conf.blk) & BDRV_O_NOCACHE)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* MMC writing cannot be done via pread/pwrite, because it sometimes
|
|
||||||
* involves writing beyond the maximum LBA or to negative LBA (lead-in).
|
* involves writing beyond the maximum LBA or to negative LBA (lead-in).
|
||||||
* And once you do these writes, reading from the block device is
|
|
||||||
* unreliable, too. It is even possible that reads deliver random data
|
|
||||||
* from the host page cache (this is probably a Linux bug).
|
|
||||||
*
|
|
||||||
* We might use scsi_disk_dma_reqops as long as no writing commands are
|
* We might use scsi_disk_dma_reqops as long as no writing commands are
|
||||||
* seen, but performance usually isn't paramount on optical media. So,
|
* seen, but performance usually isn't paramount on optical media. So,
|
||||||
* just make scsi-block operate the same as scsi-generic for them.
|
* just make scsi-block operate the same as scsi-generic for them.
|
||||||
|
@ -2627,6 +2727,54 @@ static bool scsi_block_is_passthrough(SCSIDiskState *s, uint8_t *buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int32_t scsi_block_dma_command(SCSIRequest *req, uint8_t *buf)
|
||||||
|
{
|
||||||
|
SCSIBlockReq *r = (SCSIBlockReq *)req;
|
||||||
|
r->cmd = req->cmd.buf[0];
|
||||||
|
switch (r->cmd >> 5) {
|
||||||
|
case 0:
|
||||||
|
/* 6-byte CDB. */
|
||||||
|
r->cdb1 = r->group_number = 0;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
/* 10-byte CDB. */
|
||||||
|
r->cdb1 = req->cmd.buf[1];
|
||||||
|
r->group_number = req->cmd.buf[6];
|
||||||
|
case 4:
|
||||||
|
/* 12-byte CDB. */
|
||||||
|
r->cdb1 = req->cmd.buf[1];
|
||||||
|
r->group_number = req->cmd.buf[10];
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
/* 16-byte CDB. */
|
||||||
|
r->cdb1 = req->cmd.buf[1];
|
||||||
|
r->group_number = req->cmd.buf[14];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r->cdb1 & 0xe0) {
|
||||||
|
/* Protection information is not supported. */
|
||||||
|
scsi_check_condition(&r->req, SENSE_CODE(INVALID_FIELD));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
r->req.status = &r->io_header.status;
|
||||||
|
return scsi_disk_dma_command(req, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const SCSIReqOps scsi_block_dma_reqops = {
|
||||||
|
.size = sizeof(SCSIBlockReq),
|
||||||
|
.free_req = scsi_free_request,
|
||||||
|
.send_command = scsi_block_dma_command,
|
||||||
|
.read_data = scsi_read_data,
|
||||||
|
.write_data = scsi_write_data,
|
||||||
|
.get_buf = scsi_get_buf,
|
||||||
|
.load_request = scsi_disk_load_request,
|
||||||
|
.save_request = scsi_disk_save_request,
|
||||||
|
};
|
||||||
|
|
||||||
static SCSIRequest *scsi_block_new_request(SCSIDevice *d, uint32_t tag,
|
static SCSIRequest *scsi_block_new_request(SCSIDevice *d, uint32_t tag,
|
||||||
uint32_t lun, uint8_t *buf,
|
uint32_t lun, uint8_t *buf,
|
||||||
void *hba_private)
|
void *hba_private)
|
||||||
|
@ -2637,7 +2785,7 @@ static SCSIRequest *scsi_block_new_request(SCSIDevice *d, uint32_t tag,
|
||||||
return scsi_req_alloc(&scsi_generic_req_ops, &s->qdev, tag, lun,
|
return scsi_req_alloc(&scsi_generic_req_ops, &s->qdev, tag, lun,
|
||||||
hba_private);
|
hba_private);
|
||||||
} else {
|
} else {
|
||||||
return scsi_req_alloc(&scsi_disk_dma_reqops, &s->qdev, tag, lun,
|
return scsi_req_alloc(&scsi_block_dma_reqops, &s->qdev, tag, lun,
|
||||||
hba_private);
|
hba_private);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2656,6 +2804,46 @@ static int scsi_block_parse_cdb(SCSIDevice *d, SCSICommand *cmd,
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static
|
||||||
|
BlockAIOCB *scsi_dma_readv(int64_t offset, QEMUIOVector *iov,
|
||||||
|
BlockCompletionFunc *cb, void *cb_opaque,
|
||||||
|
void *opaque)
|
||||||
|
{
|
||||||
|
SCSIDiskReq *r = opaque;
|
||||||
|
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
|
||||||
|
return blk_aio_preadv(s->qdev.conf.blk, offset, iov, 0, cb, cb_opaque);
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
BlockAIOCB *scsi_dma_writev(int64_t offset, QEMUIOVector *iov,
|
||||||
|
BlockCompletionFunc *cb, void *cb_opaque,
|
||||||
|
void *opaque)
|
||||||
|
{
|
||||||
|
SCSIDiskReq *r = opaque;
|
||||||
|
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
|
||||||
|
return blk_aio_pwritev(s->qdev.conf.blk, offset, iov, 0, cb, cb_opaque);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void scsi_disk_base_class_initfn(ObjectClass *klass, void *data)
|
||||||
|
{
|
||||||
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
|
SCSIDiskClass *sdc = SCSI_DISK_BASE_CLASS(klass);
|
||||||
|
|
||||||
|
dc->fw_name = "disk";
|
||||||
|
dc->reset = scsi_disk_reset;
|
||||||
|
sdc->dma_readv = scsi_dma_readv;
|
||||||
|
sdc->dma_writev = scsi_dma_writev;
|
||||||
|
sdc->need_fua_emulation = scsi_is_cmd_fua;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const TypeInfo scsi_disk_base_info = {
|
||||||
|
.name = TYPE_SCSI_DISK_BASE,
|
||||||
|
.parent = TYPE_SCSI_DEVICE,
|
||||||
|
.class_init = scsi_disk_base_class_initfn,
|
||||||
|
.instance_size = sizeof(SCSIDiskState),
|
||||||
|
.class_size = sizeof(SCSIDiskClass),
|
||||||
|
};
|
||||||
|
|
||||||
#define DEFINE_SCSI_DISK_PROPERTIES() \
|
#define DEFINE_SCSI_DISK_PROPERTIES() \
|
||||||
DEFINE_BLOCK_PROPERTIES(SCSIDiskState, qdev.conf), \
|
DEFINE_BLOCK_PROPERTIES(SCSIDiskState, qdev.conf), \
|
||||||
DEFINE_PROP_STRING("ver", SCSIDiskState, version), \
|
DEFINE_PROP_STRING("ver", SCSIDiskState, version), \
|
||||||
|
@ -2703,17 +2891,14 @@ static void scsi_hd_class_initfn(ObjectClass *klass, void *data)
|
||||||
sc->realize = scsi_hd_realize;
|
sc->realize = scsi_hd_realize;
|
||||||
sc->alloc_req = scsi_new_request;
|
sc->alloc_req = scsi_new_request;
|
||||||
sc->unit_attention_reported = scsi_disk_unit_attention_reported;
|
sc->unit_attention_reported = scsi_disk_unit_attention_reported;
|
||||||
dc->fw_name = "disk";
|
|
||||||
dc->desc = "virtual SCSI disk";
|
dc->desc = "virtual SCSI disk";
|
||||||
dc->reset = scsi_disk_reset;
|
|
||||||
dc->props = scsi_hd_properties;
|
dc->props = scsi_hd_properties;
|
||||||
dc->vmsd = &vmstate_scsi_disk_state;
|
dc->vmsd = &vmstate_scsi_disk_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo scsi_hd_info = {
|
static const TypeInfo scsi_hd_info = {
|
||||||
.name = "scsi-hd",
|
.name = "scsi-hd",
|
||||||
.parent = TYPE_SCSI_DEVICE,
|
.parent = TYPE_SCSI_DISK_BASE,
|
||||||
.instance_size = sizeof(SCSIDiskState),
|
|
||||||
.class_init = scsi_hd_class_initfn,
|
.class_init = scsi_hd_class_initfn,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2735,17 +2920,14 @@ static void scsi_cd_class_initfn(ObjectClass *klass, void *data)
|
||||||
sc->realize = scsi_cd_realize;
|
sc->realize = scsi_cd_realize;
|
||||||
sc->alloc_req = scsi_new_request;
|
sc->alloc_req = scsi_new_request;
|
||||||
sc->unit_attention_reported = scsi_disk_unit_attention_reported;
|
sc->unit_attention_reported = scsi_disk_unit_attention_reported;
|
||||||
dc->fw_name = "disk";
|
|
||||||
dc->desc = "virtual SCSI CD-ROM";
|
dc->desc = "virtual SCSI CD-ROM";
|
||||||
dc->reset = scsi_disk_reset;
|
|
||||||
dc->props = scsi_cd_properties;
|
dc->props = scsi_cd_properties;
|
||||||
dc->vmsd = &vmstate_scsi_disk_state;
|
dc->vmsd = &vmstate_scsi_disk_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo scsi_cd_info = {
|
static const TypeInfo scsi_cd_info = {
|
||||||
.name = "scsi-cd",
|
.name = "scsi-cd",
|
||||||
.parent = TYPE_SCSI_DEVICE,
|
.parent = TYPE_SCSI_DISK_BASE,
|
||||||
.instance_size = sizeof(SCSIDiskState),
|
|
||||||
.class_init = scsi_cd_class_initfn,
|
.class_init = scsi_cd_class_initfn,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2759,21 +2941,22 @@ static void scsi_block_class_initfn(ObjectClass *klass, void *data)
|
||||||
{
|
{
|
||||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass);
|
SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass);
|
||||||
|
SCSIDiskClass *sdc = SCSI_DISK_BASE_CLASS(klass);
|
||||||
|
|
||||||
sc->realize = scsi_block_realize;
|
sc->realize = scsi_block_realize;
|
||||||
sc->alloc_req = scsi_block_new_request;
|
sc->alloc_req = scsi_block_new_request;
|
||||||
sc->parse_cdb = scsi_block_parse_cdb;
|
sc->parse_cdb = scsi_block_parse_cdb;
|
||||||
dc->fw_name = "disk";
|
sdc->dma_readv = scsi_block_dma_readv;
|
||||||
|
sdc->dma_writev = scsi_block_dma_writev;
|
||||||
|
sdc->need_fua_emulation = scsi_block_no_fua;
|
||||||
dc->desc = "SCSI block device passthrough";
|
dc->desc = "SCSI block device passthrough";
|
||||||
dc->reset = scsi_disk_reset;
|
|
||||||
dc->props = scsi_block_properties;
|
dc->props = scsi_block_properties;
|
||||||
dc->vmsd = &vmstate_scsi_disk_state;
|
dc->vmsd = &vmstate_scsi_disk_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo scsi_block_info = {
|
static const TypeInfo scsi_block_info = {
|
||||||
.name = "scsi-block",
|
.name = "scsi-block",
|
||||||
.parent = TYPE_SCSI_DEVICE,
|
.parent = TYPE_SCSI_DISK_BASE,
|
||||||
.instance_size = sizeof(SCSIDiskState),
|
|
||||||
.class_init = scsi_block_class_initfn,
|
.class_init = scsi_block_class_initfn,
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
@ -2811,13 +2994,13 @@ static void scsi_disk_class_initfn(ObjectClass *klass, void *data)
|
||||||
|
|
||||||
static const TypeInfo scsi_disk_info = {
|
static const TypeInfo scsi_disk_info = {
|
||||||
.name = "scsi-disk",
|
.name = "scsi-disk",
|
||||||
.parent = TYPE_SCSI_DEVICE,
|
.parent = TYPE_SCSI_DISK_BASE,
|
||||||
.instance_size = sizeof(SCSIDiskState),
|
|
||||||
.class_init = scsi_disk_class_initfn,
|
.class_init = scsi_disk_class_initfn,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void scsi_disk_register_types(void)
|
static void scsi_disk_register_types(void)
|
||||||
{
|
{
|
||||||
|
type_register_static(&scsi_disk_base_info);
|
||||||
type_register_static(&scsi_hd_info);
|
type_register_static(&scsi_hd_info);
|
||||||
type_register_static(&scsi_cd_info);
|
type_register_static(&scsi_cd_info);
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
|
|
|
@ -222,6 +222,18 @@ static void scsi_read_complete(void * opaque, int ret)
|
||||||
r->buf[3] |= 0x80;
|
r->buf[3] |= 0x80;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (s->type == TYPE_DISK &&
|
||||||
|
r->req.cmd.buf[0] == INQUIRY &&
|
||||||
|
r->req.cmd.buf[2] == 0xb0) {
|
||||||
|
uint32_t max_xfer_len = blk_get_max_transfer_length(s->conf.blk);
|
||||||
|
if (max_xfer_len) {
|
||||||
|
stl_be_p(&r->buf[8], max_xfer_len);
|
||||||
|
/* Also take care of the opt xfer len. */
|
||||||
|
if (ldl_be_p(&r->buf[12]) > max_xfer_len) {
|
||||||
|
stl_be_p(&r->buf[12], max_xfer_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
scsi_req_data(&r->req, len);
|
scsi_req_data(&r->req, len);
|
||||||
scsi_req_unref(&r->req);
|
scsi_req_unref(&r->req);
|
||||||
}
|
}
|
||||||
|
|
|
@ -153,7 +153,7 @@ pvscsi_log2(uint32_t input)
|
||||||
return log;
|
return log;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static int
|
||||||
pvscsi_ring_init_data(PVSCSIRingInfo *m, PVSCSICmdDescSetupRings *ri)
|
pvscsi_ring_init_data(PVSCSIRingInfo *m, PVSCSICmdDescSetupRings *ri)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
@ -161,6 +161,10 @@ pvscsi_ring_init_data(PVSCSIRingInfo *m, PVSCSICmdDescSetupRings *ri)
|
||||||
uint32_t req_ring_size, cmp_ring_size;
|
uint32_t req_ring_size, cmp_ring_size;
|
||||||
m->rs_pa = ri->ringsStatePPN << VMW_PAGE_SHIFT;
|
m->rs_pa = ri->ringsStatePPN << VMW_PAGE_SHIFT;
|
||||||
|
|
||||||
|
if ((ri->reqRingNumPages > PVSCSI_SETUP_RINGS_MAX_NUM_PAGES)
|
||||||
|
|| (ri->cmpRingNumPages > PVSCSI_SETUP_RINGS_MAX_NUM_PAGES)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
req_ring_size = ri->reqRingNumPages * PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE;
|
req_ring_size = ri->reqRingNumPages * PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE;
|
||||||
cmp_ring_size = ri->cmpRingNumPages * PVSCSI_MAX_NUM_CMP_ENTRIES_PER_PAGE;
|
cmp_ring_size = ri->cmpRingNumPages * PVSCSI_MAX_NUM_CMP_ENTRIES_PER_PAGE;
|
||||||
txr_len_log2 = pvscsi_log2(req_ring_size - 1);
|
txr_len_log2 = pvscsi_log2(req_ring_size - 1);
|
||||||
|
@ -192,15 +196,20 @@ pvscsi_ring_init_data(PVSCSIRingInfo *m, PVSCSICmdDescSetupRings *ri)
|
||||||
|
|
||||||
/* Flush ring state page changes */
|
/* Flush ring state page changes */
|
||||||
smp_wmb();
|
smp_wmb();
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static int
|
||||||
pvscsi_ring_init_msg(PVSCSIRingInfo *m, PVSCSICmdDescSetupMsgRing *ri)
|
pvscsi_ring_init_msg(PVSCSIRingInfo *m, PVSCSICmdDescSetupMsgRing *ri)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
uint32_t len_log2;
|
uint32_t len_log2;
|
||||||
uint32_t ring_size;
|
uint32_t ring_size;
|
||||||
|
|
||||||
|
if (ri->numPages > PVSCSI_SETUP_MSG_RING_MAX_NUM_PAGES) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
ring_size = ri->numPages * PVSCSI_MAX_NUM_MSG_ENTRIES_PER_PAGE;
|
ring_size = ri->numPages * PVSCSI_MAX_NUM_MSG_ENTRIES_PER_PAGE;
|
||||||
len_log2 = pvscsi_log2(ring_size - 1);
|
len_log2 = pvscsi_log2(ring_size - 1);
|
||||||
|
|
||||||
|
@ -220,6 +229,8 @@ pvscsi_ring_init_msg(PVSCSIRingInfo *m, PVSCSICmdDescSetupMsgRing *ri)
|
||||||
|
|
||||||
/* Flush ring state page changes */
|
/* Flush ring state page changes */
|
||||||
smp_wmb();
|
smp_wmb();
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -770,7 +781,10 @@ pvscsi_on_cmd_setup_rings(PVSCSIState *s)
|
||||||
trace_pvscsi_on_cmd_arrived("PVSCSI_CMD_SETUP_RINGS");
|
trace_pvscsi_on_cmd_arrived("PVSCSI_CMD_SETUP_RINGS");
|
||||||
|
|
||||||
pvscsi_dbg_dump_tx_rings_config(rc);
|
pvscsi_dbg_dump_tx_rings_config(rc);
|
||||||
pvscsi_ring_init_data(&s->rings, rc);
|
if (pvscsi_ring_init_data(&s->rings, rc) < 0) {
|
||||||
|
return PVSCSI_COMMAND_PROCESSING_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
s->rings_info_valid = TRUE;
|
s->rings_info_valid = TRUE;
|
||||||
return PVSCSI_COMMAND_PROCESSING_SUCCEEDED;
|
return PVSCSI_COMMAND_PROCESSING_SUCCEEDED;
|
||||||
}
|
}
|
||||||
|
@ -850,7 +864,9 @@ pvscsi_on_cmd_setup_msg_ring(PVSCSIState *s)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s->rings_info_valid) {
|
if (s->rings_info_valid) {
|
||||||
pvscsi_ring_init_msg(&s->rings, rc);
|
if (pvscsi_ring_init_msg(&s->rings, rc) < 0) {
|
||||||
|
return PVSCSI_COMMAND_PROCESSING_FAILED;
|
||||||
|
}
|
||||||
s->msg_ring_info_valid = TRUE;
|
s->msg_ring_info_valid = TRUE;
|
||||||
}
|
}
|
||||||
return sizeof(PVSCSICmdDescSetupMsgRing) / sizeof(uint32_t);
|
return sizeof(PVSCSICmdDescSetupMsgRing) / sizeof(uint32_t);
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
#include "sysemu/kvm.h"
|
#include "sysemu/kvm.h"
|
||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
#include "qemu/sockets.h"
|
#include "qemu/sockets.h"
|
||||||
#include "exec/ram_addr.h"
|
|
||||||
#include "migration/migration.h"
|
#include "migration/migration.h"
|
||||||
|
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
|
@ -247,18 +246,18 @@ static int vhost_user_set_mem_table(struct vhost_dev *dev,
|
||||||
|
|
||||||
for (i = 0; i < dev->mem->nregions; ++i) {
|
for (i = 0; i < dev->mem->nregions; ++i) {
|
||||||
struct vhost_memory_region *reg = dev->mem->regions + i;
|
struct vhost_memory_region *reg = dev->mem->regions + i;
|
||||||
ram_addr_t ram_addr;
|
ram_addr_t offset;
|
||||||
|
MemoryRegion *mr;
|
||||||
|
|
||||||
assert((uintptr_t)reg->userspace_addr == reg->userspace_addr);
|
assert((uintptr_t)reg->userspace_addr == reg->userspace_addr);
|
||||||
qemu_ram_addr_from_host((void *)(uintptr_t)reg->userspace_addr,
|
mr = memory_region_from_host((void *)(uintptr_t)reg->userspace_addr,
|
||||||
&ram_addr);
|
&offset);
|
||||||
fd = qemu_get_ram_fd(ram_addr);
|
fd = memory_region_get_fd(mr);
|
||||||
if (fd > 0) {
|
if (fd > 0) {
|
||||||
msg.payload.memory.regions[fd_num].userspace_addr = reg->userspace_addr;
|
msg.payload.memory.regions[fd_num].userspace_addr = reg->userspace_addr;
|
||||||
msg.payload.memory.regions[fd_num].memory_size = reg->memory_size;
|
msg.payload.memory.regions[fd_num].memory_size = reg->memory_size;
|
||||||
msg.payload.memory.regions[fd_num].guest_phys_addr = reg->guest_phys_addr;
|
msg.payload.memory.regions[fd_num].guest_phys_addr = reg->guest_phys_addr;
|
||||||
msg.payload.memory.regions[fd_num].mmap_offset = reg->userspace_addr -
|
msg.payload.memory.regions[fd_num].mmap_offset = offset;
|
||||||
(uintptr_t) qemu_get_ram_block_host_ptr(ram_addr);
|
|
||||||
assert(fd_num < VHOST_MEMORY_MAX_NREGIONS);
|
assert(fd_num < VHOST_MEMORY_MAX_NREGIONS);
|
||||||
fds[fd_num++] = fd;
|
fds[fd_num++] = fd;
|
||||||
}
|
}
|
||||||
|
@ -616,17 +615,15 @@ static bool vhost_user_can_merge(struct vhost_dev *dev,
|
||||||
uint64_t start1, uint64_t size1,
|
uint64_t start1, uint64_t size1,
|
||||||
uint64_t start2, uint64_t size2)
|
uint64_t start2, uint64_t size2)
|
||||||
{
|
{
|
||||||
ram_addr_t ram_addr;
|
ram_addr_t offset;
|
||||||
int mfd, rfd;
|
int mfd, rfd;
|
||||||
MemoryRegion *mr;
|
MemoryRegion *mr;
|
||||||
|
|
||||||
mr = qemu_ram_addr_from_host((void *)(uintptr_t)start1, &ram_addr);
|
mr = memory_region_from_host((void *)(uintptr_t)start1, &offset);
|
||||||
assert(mr);
|
mfd = memory_region_get_fd(mr);
|
||||||
mfd = qemu_get_ram_fd(ram_addr);
|
|
||||||
|
|
||||||
mr = qemu_ram_addr_from_host((void *)(uintptr_t)start2, &ram_addr);
|
mr = memory_region_from_host((void *)(uintptr_t)start2, &offset);
|
||||||
assert(mr);
|
rfd = memory_region_get_fd(mr);
|
||||||
rfd = qemu_get_ram_fd(ram_addr);
|
|
||||||
|
|
||||||
return mfd == rfd;
|
return mfd == rfd;
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,10 +57,10 @@ typedef uint32_t CPUReadMemoryFunc(void *opaque, hwaddr addr);
|
||||||
|
|
||||||
void qemu_ram_remap(ram_addr_t addr, ram_addr_t length);
|
void qemu_ram_remap(ram_addr_t addr, ram_addr_t length);
|
||||||
/* This should not be used by devices. */
|
/* This should not be used by devices. */
|
||||||
MemoryRegion *qemu_ram_addr_from_host(void *ptr, ram_addr_t *ram_addr);
|
ram_addr_t qemu_ram_addr_from_host(void *ptr);
|
||||||
RAMBlock *qemu_ram_block_by_name(const char *name);
|
RAMBlock *qemu_ram_block_by_name(const char *name);
|
||||||
RAMBlock *qemu_ram_block_from_host(void *ptr, bool round_offset,
|
RAMBlock *qemu_ram_block_from_host(void *ptr, bool round_offset,
|
||||||
ram_addr_t *ram_addr, ram_addr_t *offset);
|
ram_addr_t *offset);
|
||||||
void qemu_ram_set_idstr(RAMBlock *block, const char *name, DeviceState *dev);
|
void qemu_ram_set_idstr(RAMBlock *block, const char *name, DeviceState *dev);
|
||||||
void qemu_ram_unset_idstr(RAMBlock *block);
|
void qemu_ram_unset_idstr(RAMBlock *block);
|
||||||
const char *qemu_ram_get_idstr(RAMBlock *rb);
|
const char *qemu_ram_get_idstr(RAMBlock *rb);
|
||||||
|
|
|
@ -32,6 +32,8 @@
|
||||||
#include "qom/object.h"
|
#include "qom/object.h"
|
||||||
#include "qemu/rcu.h"
|
#include "qemu/rcu.h"
|
||||||
|
|
||||||
|
#define RAM_ADDR_INVALID (~(ram_addr_t)0)
|
||||||
|
|
||||||
#define MAX_PHYS_ADDR_SPACE_BITS 62
|
#define MAX_PHYS_ADDR_SPACE_BITS 62
|
||||||
#define MAX_PHYS_ADDR (((hwaddr)1 << MAX_PHYS_ADDR_SPACE_BITS) - 1)
|
#define MAX_PHYS_ADDR (((hwaddr)1 << MAX_PHYS_ADDR_SPACE_BITS) - 1)
|
||||||
|
|
||||||
|
@ -666,6 +668,35 @@ static inline bool memory_region_is_rom(MemoryRegion *mr)
|
||||||
*/
|
*/
|
||||||
int memory_region_get_fd(MemoryRegion *mr);
|
int memory_region_get_fd(MemoryRegion *mr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* memory_region_set_fd: Mark a RAM memory region as backed by a
|
||||||
|
* file descriptor.
|
||||||
|
*
|
||||||
|
* This function is typically used after memory_region_init_ram_ptr().
|
||||||
|
*
|
||||||
|
* @mr: the memory region being queried.
|
||||||
|
* @fd: the file descriptor that backs @mr.
|
||||||
|
*/
|
||||||
|
void memory_region_set_fd(MemoryRegion *mr, int fd);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* memory_region_from_host: Convert a pointer into a RAM memory region
|
||||||
|
* and an offset within it.
|
||||||
|
*
|
||||||
|
* Given a host pointer inside a RAM memory region (created with
|
||||||
|
* memory_region_init_ram() or memory_region_init_ram_ptr()), return
|
||||||
|
* the MemoryRegion and the offset within it.
|
||||||
|
*
|
||||||
|
* Use with care; by the time this function returns, the returned pointer is
|
||||||
|
* not protected by RCU anymore. If the caller is not within an RCU critical
|
||||||
|
* section and does not hold the iothread lock, it must have other means of
|
||||||
|
* protecting the pointer, such as a reference to the region that includes
|
||||||
|
* the incoming ram_addr_t.
|
||||||
|
*
|
||||||
|
* @mr: the memory region being queried.
|
||||||
|
*/
|
||||||
|
MemoryRegion *memory_region_from_host(void *ptr, ram_addr_t *offset);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* memory_region_get_ram_ptr: Get a pointer into a RAM memory region.
|
* memory_region_get_ram_ptr: Get a pointer into a RAM memory region.
|
||||||
*
|
*
|
||||||
|
@ -1362,7 +1393,7 @@ MemTxResult address_space_read_continue(AddressSpace *as, hwaddr addr,
|
||||||
MemoryRegion *mr);
|
MemoryRegion *mr);
|
||||||
MemTxResult address_space_read_full(AddressSpace *as, hwaddr addr,
|
MemTxResult address_space_read_full(AddressSpace *as, hwaddr addr,
|
||||||
MemTxAttrs attrs, uint8_t *buf, int len);
|
MemTxAttrs attrs, uint8_t *buf, int len);
|
||||||
void *qemu_get_ram_ptr(RAMBlock *ram_block, ram_addr_t addr);
|
void *qemu_map_ram_ptr(RAMBlock *ram_block, ram_addr_t addr);
|
||||||
|
|
||||||
static inline bool memory_access_is_direct(MemoryRegion *mr, bool is_write)
|
static inline bool memory_access_is_direct(MemoryRegion *mr, bool is_write)
|
||||||
{
|
{
|
||||||
|
@ -1400,8 +1431,7 @@ MemTxResult address_space_read(AddressSpace *as, hwaddr addr, MemTxAttrs attrs,
|
||||||
l = len;
|
l = len;
|
||||||
mr = address_space_translate(as, addr, &addr1, &l, false);
|
mr = address_space_translate(as, addr, &addr1, &l, false);
|
||||||
if (len == l && memory_access_is_direct(mr, false)) {
|
if (len == l && memory_access_is_direct(mr, false)) {
|
||||||
addr1 += memory_region_get_ram_addr(mr);
|
ptr = qemu_map_ram_ptr(mr->ram_block, addr1);
|
||||||
ptr = qemu_get_ram_ptr(mr->ram_block, addr1);
|
|
||||||
memcpy(buf, ptr, len);
|
memcpy(buf, ptr, len);
|
||||||
} else {
|
} else {
|
||||||
result = address_space_read_continue(as, addr, attrs, buf, len,
|
result = address_space_read_continue(as, addr, attrs, buf, len,
|
||||||
|
|
|
@ -105,9 +105,6 @@ RAMBlock *qemu_ram_alloc_resizeable(ram_addr_t size, ram_addr_t max_size,
|
||||||
uint64_t length,
|
uint64_t length,
|
||||||
void *host),
|
void *host),
|
||||||
MemoryRegion *mr, Error **errp);
|
MemoryRegion *mr, Error **errp);
|
||||||
int qemu_get_ram_fd(ram_addr_t addr);
|
|
||||||
void qemu_set_ram_fd(ram_addr_t addr, int fd);
|
|
||||||
void *qemu_get_ram_block_host_ptr(ram_addr_t addr);
|
|
||||||
void qemu_ram_free(RAMBlock *block);
|
void qemu_ram_free(RAMBlock *block);
|
||||||
|
|
||||||
int qemu_ram_resize(RAMBlock *block, ram_addr_t newsize, Error **errp);
|
int qemu_ram_resize(RAMBlock *block, ram_addr_t newsize, Error **errp);
|
||||||
|
|
|
@ -46,4 +46,20 @@ etraxfs_eth_init(NICInfo *nd, hwaddr base, int phyaddr,
|
||||||
return dev;
|
return dev;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline DeviceState *etraxfs_ser_create(hwaddr addr,
|
||||||
|
qemu_irq irq,
|
||||||
|
CharDriverState *chr)
|
||||||
|
{
|
||||||
|
DeviceState *dev;
|
||||||
|
SysBusDevice *s;
|
||||||
|
|
||||||
|
dev = qdev_create(NULL, "etraxfs,serial");
|
||||||
|
s = SYS_BUS_DEVICE(dev);
|
||||||
|
qdev_prop_set_chr(dev, "chardev", chr);
|
||||||
|
qdev_init_nofail(dev);
|
||||||
|
sysbus_mmio_map(s, 0, addr);
|
||||||
|
sysbus_connect_irq(s, 0, irq);
|
||||||
|
return dev;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -36,7 +36,18 @@
|
||||||
#define smp_wmb() ({ barrier(); __atomic_thread_fence(__ATOMIC_RELEASE); barrier(); })
|
#define smp_wmb() ({ barrier(); __atomic_thread_fence(__ATOMIC_RELEASE); barrier(); })
|
||||||
#define smp_rmb() ({ barrier(); __atomic_thread_fence(__ATOMIC_ACQUIRE); barrier(); })
|
#define smp_rmb() ({ barrier(); __atomic_thread_fence(__ATOMIC_ACQUIRE); barrier(); })
|
||||||
|
|
||||||
|
/* Most compilers currently treat consume and acquire the same, but really
|
||||||
|
* no processors except Alpha need a barrier here. Leave it in if
|
||||||
|
* using Thread Sanitizer to avoid warnings, otherwise optimize it away.
|
||||||
|
*/
|
||||||
|
#if defined(__SANITIZE_THREAD__)
|
||||||
#define smp_read_barrier_depends() ({ barrier(); __atomic_thread_fence(__ATOMIC_CONSUME); barrier(); })
|
#define smp_read_barrier_depends() ({ barrier(); __atomic_thread_fence(__ATOMIC_CONSUME); barrier(); })
|
||||||
|
#elsif defined(__alpha__)
|
||||||
|
#define smp_read_barrier_depends() asm volatile("mb":::"memory")
|
||||||
|
#else
|
||||||
|
#define smp_read_barrier_depends() barrier()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/* Weak atomic operations prevent the compiler moving other
|
/* Weak atomic operations prevent the compiler moving other
|
||||||
* loads/stores past the atomic operation load/store. However there is
|
* loads/stores past the atomic operation load/store. However there is
|
||||||
|
@ -56,13 +67,23 @@
|
||||||
__atomic_store(ptr, &_val, __ATOMIC_RELAXED); \
|
__atomic_store(ptr, &_val, __ATOMIC_RELAXED); \
|
||||||
} while(0)
|
} while(0)
|
||||||
|
|
||||||
/* Atomic RCU operations imply weak memory barriers */
|
/* See above: most compilers currently treat consume and acquire the
|
||||||
|
* same, but this slows down atomic_rcu_read unnecessarily.
|
||||||
|
*/
|
||||||
|
#ifdef __SANITIZE_THREAD__
|
||||||
|
#define atomic_rcu_read__nocheck(ptr, valptr) \
|
||||||
|
__atomic_load(ptr, valptr, __ATOMIC_CONSUME);
|
||||||
|
#else
|
||||||
|
#define atomic_rcu_read__nocheck(ptr, valptr) \
|
||||||
|
__atomic_load(ptr, valptr, __ATOMIC_RELAXED); \
|
||||||
|
smp_read_barrier_depends();
|
||||||
|
#endif
|
||||||
|
|
||||||
#define atomic_rcu_read(ptr) \
|
#define atomic_rcu_read(ptr) \
|
||||||
({ \
|
({ \
|
||||||
QEMU_BUILD_BUG_ON(sizeof(*ptr) > sizeof(void *)); \
|
QEMU_BUILD_BUG_ON(sizeof(*ptr) > sizeof(void *)); \
|
||||||
typeof(*ptr) _val; \
|
typeof(*ptr) _val; \
|
||||||
__atomic_load(ptr, &_val, __ATOMIC_CONSUME); \
|
atomic_rcu_read__nocheck(ptr, &_val); \
|
||||||
_val; \
|
_val; \
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
43
memory.c
43
memory.c
|
@ -33,8 +33,6 @@
|
||||||
|
|
||||||
//#define DEBUG_UNASSIGNED
|
//#define DEBUG_UNASSIGNED
|
||||||
|
|
||||||
#define RAM_ADDR_INVALID (~(ram_addr_t)0)
|
|
||||||
|
|
||||||
static unsigned memory_region_transaction_depth;
|
static unsigned memory_region_transaction_depth;
|
||||||
static bool memory_region_update_pending;
|
static bool memory_region_update_pending;
|
||||||
static bool ioeventfd_update_pending;
|
static bool ioeventfd_update_pending;
|
||||||
|
@ -227,6 +225,7 @@ struct FlatRange {
|
||||||
hwaddr offset_in_region;
|
hwaddr offset_in_region;
|
||||||
AddrRange addr;
|
AddrRange addr;
|
||||||
uint8_t dirty_log_mask;
|
uint8_t dirty_log_mask;
|
||||||
|
bool romd_mode;
|
||||||
bool readonly;
|
bool readonly;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -251,6 +250,7 @@ static bool flatrange_equal(FlatRange *a, FlatRange *b)
|
||||||
return a->mr == b->mr
|
return a->mr == b->mr
|
||||||
&& addrrange_equal(a->addr, b->addr)
|
&& addrrange_equal(a->addr, b->addr)
|
||||||
&& a->offset_in_region == b->offset_in_region
|
&& a->offset_in_region == b->offset_in_region
|
||||||
|
&& a->romd_mode == b->romd_mode
|
||||||
&& a->readonly == b->readonly;
|
&& a->readonly == b->readonly;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -310,6 +310,7 @@ static bool can_merge(FlatRange *r1, FlatRange *r2)
|
||||||
r1->addr.size),
|
r1->addr.size),
|
||||||
int128_make64(r2->offset_in_region))
|
int128_make64(r2->offset_in_region))
|
||||||
&& r1->dirty_log_mask == r2->dirty_log_mask
|
&& r1->dirty_log_mask == r2->dirty_log_mask
|
||||||
|
&& r1->romd_mode == r2->romd_mode
|
||||||
&& r1->readonly == r2->readonly;
|
&& r1->readonly == r2->readonly;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -663,6 +664,7 @@ static void render_memory_region(FlatView *view,
|
||||||
|
|
||||||
fr.mr = mr;
|
fr.mr = mr;
|
||||||
fr.dirty_log_mask = memory_region_get_dirty_log_mask(mr);
|
fr.dirty_log_mask = memory_region_get_dirty_log_mask(mr);
|
||||||
|
fr.romd_mode = mr->romd_mode;
|
||||||
fr.readonly = readonly;
|
fr.readonly = readonly;
|
||||||
|
|
||||||
/* Render the region itself into any gaps left by the current view. */
|
/* Render the region itself into any gaps left by the current view. */
|
||||||
|
@ -1622,13 +1624,26 @@ void memory_region_reset_dirty(MemoryRegion *mr, hwaddr addr,
|
||||||
|
|
||||||
int memory_region_get_fd(MemoryRegion *mr)
|
int memory_region_get_fd(MemoryRegion *mr)
|
||||||
{
|
{
|
||||||
if (mr->alias) {
|
int fd;
|
||||||
return memory_region_get_fd(mr->alias);
|
|
||||||
|
rcu_read_lock();
|
||||||
|
while (mr->alias) {
|
||||||
|
mr = mr->alias;
|
||||||
}
|
}
|
||||||
|
fd = mr->ram_block->fd;
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
assert(mr->ram_block);
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
return qemu_get_ram_fd(memory_region_get_ram_addr(mr));
|
void memory_region_set_fd(MemoryRegion *mr, int fd)
|
||||||
|
{
|
||||||
|
rcu_read_lock();
|
||||||
|
while (mr->alias) {
|
||||||
|
mr = mr->alias;
|
||||||
|
}
|
||||||
|
mr->ram_block->fd = fd;
|
||||||
|
rcu_read_unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void *memory_region_get_ram_ptr(MemoryRegion *mr)
|
void *memory_region_get_ram_ptr(MemoryRegion *mr)
|
||||||
|
@ -1642,10 +1657,22 @@ void *memory_region_get_ram_ptr(MemoryRegion *mr)
|
||||||
mr = mr->alias;
|
mr = mr->alias;
|
||||||
}
|
}
|
||||||
assert(mr->ram_block);
|
assert(mr->ram_block);
|
||||||
ptr = qemu_get_ram_ptr(mr->ram_block, memory_region_get_ram_addr(mr));
|
ptr = qemu_map_ram_ptr(mr->ram_block, offset);
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
|
|
||||||
return ptr + offset;
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
MemoryRegion *memory_region_from_host(void *ptr, ram_addr_t *offset)
|
||||||
|
{
|
||||||
|
RAMBlock *block;
|
||||||
|
|
||||||
|
block = qemu_ram_block_from_host(ptr, false, offset);
|
||||||
|
if (!block) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return block->mr;
|
||||||
}
|
}
|
||||||
|
|
||||||
ram_addr_t memory_region_get_ram_addr(MemoryRegion *mr)
|
ram_addr_t memory_region_get_ram_addr(MemoryRegion *mr)
|
||||||
|
|
|
@ -407,7 +407,6 @@ static void *postcopy_ram_fault_thread(void *opaque)
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
ram_addr_t rb_offset;
|
ram_addr_t rb_offset;
|
||||||
ram_addr_t in_raspace;
|
|
||||||
struct pollfd pfd[2];
|
struct pollfd pfd[2];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -459,7 +458,7 @@ static void *postcopy_ram_fault_thread(void *opaque)
|
||||||
|
|
||||||
rb = qemu_ram_block_from_host(
|
rb = qemu_ram_block_from_host(
|
||||||
(void *)(uintptr_t)msg.arg.pagefault.address,
|
(void *)(uintptr_t)msg.arg.pagefault.address,
|
||||||
true, &in_raspace, &rb_offset);
|
true, &rb_offset);
|
||||||
if (!rb) {
|
if (!rb) {
|
||||||
error_report("postcopy_ram_fault_thread: Fault outside guest: %"
|
error_report("postcopy_ram_fault_thread: Fault outside guest: %"
|
||||||
PRIx64, (uint64_t)msg.arg.pagefault.address);
|
PRIx64, (uint64_t)msg.arg.pagefault.address);
|
||||||
|
|
20
nbd/server.c
20
nbd/server.c
|
@ -1153,12 +1153,20 @@ static void nbd_trip(void *opaque)
|
||||||
break;
|
break;
|
||||||
case NBD_CMD_TRIM:
|
case NBD_CMD_TRIM:
|
||||||
TRACE("Request type is TRIM");
|
TRACE("Request type is TRIM");
|
||||||
ret = blk_co_discard(exp->blk, (request.from + exp->dev_offset)
|
/* Ignore unaligned head or tail, until block layer adds byte
|
||||||
/ BDRV_SECTOR_SIZE,
|
* interface */
|
||||||
request.len / BDRV_SECTOR_SIZE);
|
if (request.len >= BDRV_SECTOR_SIZE) {
|
||||||
if (ret < 0) {
|
request.len -= (request.from + request.len) % BDRV_SECTOR_SIZE;
|
||||||
LOG("discard failed");
|
ret = blk_co_discard(exp->blk,
|
||||||
reply.error = -ret;
|
DIV_ROUND_UP(request.from + exp->dev_offset,
|
||||||
|
BDRV_SECTOR_SIZE),
|
||||||
|
request.len / BDRV_SECTOR_SIZE);
|
||||||
|
if (ret < 0) {
|
||||||
|
LOG("discard failed");
|
||||||
|
reply.error = -ret;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TRACE("trim request too small, ignoring");
|
||||||
}
|
}
|
||||||
if (nbd_co_send_reply(req, &reply, 0) < 0) {
|
if (nbd_co_send_reply(req, &reply, 0) < 0) {
|
||||||
goto out;
|
goto out;
|
||||||
|
|
|
@ -328,23 +328,10 @@ def qlist_foreach(head, field_str):
|
||||||
yield var
|
yield var
|
||||||
|
|
||||||
|
|
||||||
def qemu_get_ram_block(ram_addr):
|
def qemu_map_ram_ptr(block, offset):
|
||||||
"""Returns the RAMBlock struct to which the given address belongs."""
|
|
||||||
|
|
||||||
ram_blocks = gdb.parse_and_eval("ram_list.blocks")
|
|
||||||
|
|
||||||
for block in qlist_foreach(ram_blocks, "next"):
|
|
||||||
if (ram_addr - block["offset"]) < block["used_length"]:
|
|
||||||
return block
|
|
||||||
|
|
||||||
raise gdb.GdbError("Bad ram offset %x" % ram_addr)
|
|
||||||
|
|
||||||
|
|
||||||
def qemu_get_ram_ptr(ram_addr):
|
|
||||||
"""Returns qemu vaddr for given guest physical address."""
|
"""Returns qemu vaddr for given guest physical address."""
|
||||||
|
|
||||||
block = qemu_get_ram_block(ram_addr)
|
return block["host"] + offset
|
||||||
return block["host"] + (ram_addr - block["offset"])
|
|
||||||
|
|
||||||
|
|
||||||
def memory_region_get_ram_ptr(memory_region):
|
def memory_region_get_ram_ptr(memory_region):
|
||||||
|
@ -352,7 +339,7 @@ def memory_region_get_ram_ptr(memory_region):
|
||||||
return (memory_region_get_ram_ptr(memory_region["alias"].dereference())
|
return (memory_region_get_ram_ptr(memory_region["alias"].dereference())
|
||||||
+ memory_region["alias_offset"])
|
+ memory_region["alias_offset"])
|
||||||
|
|
||||||
return qemu_get_ram_ptr(memory_region["ram_block"]["offset"])
|
return qemu_map_ram_ptr(memory_region["ram_block"], 0)
|
||||||
|
|
||||||
|
|
||||||
def get_guest_phys_blocks():
|
def get_guest_phys_blocks():
|
||||||
|
|
|
@ -1,825 +0,0 @@
|
||||||
#!/usr/bin/python
|
|
||||||
#
|
|
||||||
# top-like utility for displaying kvm statistics
|
|
||||||
#
|
|
||||||
# Copyright 2006-2008 Qumranet Technologies
|
|
||||||
# Copyright 2008-2011 Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# Authors:
|
|
||||||
# Avi Kivity <avi@redhat.com>
|
|
||||||
#
|
|
||||||
# This work is licensed under the terms of the GNU GPL, version 2. See
|
|
||||||
# the COPYING file in the top-level directory.
|
|
||||||
|
|
||||||
import curses
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import time
|
|
||||||
import optparse
|
|
||||||
import ctypes
|
|
||||||
import fcntl
|
|
||||||
import resource
|
|
||||||
import struct
|
|
||||||
import re
|
|
||||||
from collections import defaultdict
|
|
||||||
from time import sleep
|
|
||||||
|
|
||||||
VMX_EXIT_REASONS = {
|
|
||||||
'EXCEPTION_NMI': 0,
|
|
||||||
'EXTERNAL_INTERRUPT': 1,
|
|
||||||
'TRIPLE_FAULT': 2,
|
|
||||||
'PENDING_INTERRUPT': 7,
|
|
||||||
'NMI_WINDOW': 8,
|
|
||||||
'TASK_SWITCH': 9,
|
|
||||||
'CPUID': 10,
|
|
||||||
'HLT': 12,
|
|
||||||
'INVLPG': 14,
|
|
||||||
'RDPMC': 15,
|
|
||||||
'RDTSC': 16,
|
|
||||||
'VMCALL': 18,
|
|
||||||
'VMCLEAR': 19,
|
|
||||||
'VMLAUNCH': 20,
|
|
||||||
'VMPTRLD': 21,
|
|
||||||
'VMPTRST': 22,
|
|
||||||
'VMREAD': 23,
|
|
||||||
'VMRESUME': 24,
|
|
||||||
'VMWRITE': 25,
|
|
||||||
'VMOFF': 26,
|
|
||||||
'VMON': 27,
|
|
||||||
'CR_ACCESS': 28,
|
|
||||||
'DR_ACCESS': 29,
|
|
||||||
'IO_INSTRUCTION': 30,
|
|
||||||
'MSR_READ': 31,
|
|
||||||
'MSR_WRITE': 32,
|
|
||||||
'INVALID_STATE': 33,
|
|
||||||
'MWAIT_INSTRUCTION': 36,
|
|
||||||
'MONITOR_INSTRUCTION': 39,
|
|
||||||
'PAUSE_INSTRUCTION': 40,
|
|
||||||
'MCE_DURING_VMENTRY': 41,
|
|
||||||
'TPR_BELOW_THRESHOLD': 43,
|
|
||||||
'APIC_ACCESS': 44,
|
|
||||||
'EPT_VIOLATION': 48,
|
|
||||||
'EPT_MISCONFIG': 49,
|
|
||||||
'WBINVD': 54,
|
|
||||||
'XSETBV': 55,
|
|
||||||
'APIC_WRITE': 56,
|
|
||||||
'INVPCID': 58,
|
|
||||||
}
|
|
||||||
|
|
||||||
SVM_EXIT_REASONS = {
|
|
||||||
'READ_CR0': 0x000,
|
|
||||||
'READ_CR3': 0x003,
|
|
||||||
'READ_CR4': 0x004,
|
|
||||||
'READ_CR8': 0x008,
|
|
||||||
'WRITE_CR0': 0x010,
|
|
||||||
'WRITE_CR3': 0x013,
|
|
||||||
'WRITE_CR4': 0x014,
|
|
||||||
'WRITE_CR8': 0x018,
|
|
||||||
'READ_DR0': 0x020,
|
|
||||||
'READ_DR1': 0x021,
|
|
||||||
'READ_DR2': 0x022,
|
|
||||||
'READ_DR3': 0x023,
|
|
||||||
'READ_DR4': 0x024,
|
|
||||||
'READ_DR5': 0x025,
|
|
||||||
'READ_DR6': 0x026,
|
|
||||||
'READ_DR7': 0x027,
|
|
||||||
'WRITE_DR0': 0x030,
|
|
||||||
'WRITE_DR1': 0x031,
|
|
||||||
'WRITE_DR2': 0x032,
|
|
||||||
'WRITE_DR3': 0x033,
|
|
||||||
'WRITE_DR4': 0x034,
|
|
||||||
'WRITE_DR5': 0x035,
|
|
||||||
'WRITE_DR6': 0x036,
|
|
||||||
'WRITE_DR7': 0x037,
|
|
||||||
'EXCP_BASE': 0x040,
|
|
||||||
'INTR': 0x060,
|
|
||||||
'NMI': 0x061,
|
|
||||||
'SMI': 0x062,
|
|
||||||
'INIT': 0x063,
|
|
||||||
'VINTR': 0x064,
|
|
||||||
'CR0_SEL_WRITE': 0x065,
|
|
||||||
'IDTR_READ': 0x066,
|
|
||||||
'GDTR_READ': 0x067,
|
|
||||||
'LDTR_READ': 0x068,
|
|
||||||
'TR_READ': 0x069,
|
|
||||||
'IDTR_WRITE': 0x06a,
|
|
||||||
'GDTR_WRITE': 0x06b,
|
|
||||||
'LDTR_WRITE': 0x06c,
|
|
||||||
'TR_WRITE': 0x06d,
|
|
||||||
'RDTSC': 0x06e,
|
|
||||||
'RDPMC': 0x06f,
|
|
||||||
'PUSHF': 0x070,
|
|
||||||
'POPF': 0x071,
|
|
||||||
'CPUID': 0x072,
|
|
||||||
'RSM': 0x073,
|
|
||||||
'IRET': 0x074,
|
|
||||||
'SWINT': 0x075,
|
|
||||||
'INVD': 0x076,
|
|
||||||
'PAUSE': 0x077,
|
|
||||||
'HLT': 0x078,
|
|
||||||
'INVLPG': 0x079,
|
|
||||||
'INVLPGA': 0x07a,
|
|
||||||
'IOIO': 0x07b,
|
|
||||||
'MSR': 0x07c,
|
|
||||||
'TASK_SWITCH': 0x07d,
|
|
||||||
'FERR_FREEZE': 0x07e,
|
|
||||||
'SHUTDOWN': 0x07f,
|
|
||||||
'VMRUN': 0x080,
|
|
||||||
'VMMCALL': 0x081,
|
|
||||||
'VMLOAD': 0x082,
|
|
||||||
'VMSAVE': 0x083,
|
|
||||||
'STGI': 0x084,
|
|
||||||
'CLGI': 0x085,
|
|
||||||
'SKINIT': 0x086,
|
|
||||||
'RDTSCP': 0x087,
|
|
||||||
'ICEBP': 0x088,
|
|
||||||
'WBINVD': 0x089,
|
|
||||||
'MONITOR': 0x08a,
|
|
||||||
'MWAIT': 0x08b,
|
|
||||||
'MWAIT_COND': 0x08c,
|
|
||||||
'XSETBV': 0x08d,
|
|
||||||
'NPF': 0x400,
|
|
||||||
}
|
|
||||||
|
|
||||||
# EC definition of HSR (from arch/arm64/include/asm/kvm_arm.h)
|
|
||||||
AARCH64_EXIT_REASONS = {
|
|
||||||
'UNKNOWN': 0x00,
|
|
||||||
'WFI': 0x01,
|
|
||||||
'CP15_32': 0x03,
|
|
||||||
'CP15_64': 0x04,
|
|
||||||
'CP14_MR': 0x05,
|
|
||||||
'CP14_LS': 0x06,
|
|
||||||
'FP_ASIMD': 0x07,
|
|
||||||
'CP10_ID': 0x08,
|
|
||||||
'CP14_64': 0x0C,
|
|
||||||
'ILL_ISS': 0x0E,
|
|
||||||
'SVC32': 0x11,
|
|
||||||
'HVC32': 0x12,
|
|
||||||
'SMC32': 0x13,
|
|
||||||
'SVC64': 0x15,
|
|
||||||
'HVC64': 0x16,
|
|
||||||
'SMC64': 0x17,
|
|
||||||
'SYS64': 0x18,
|
|
||||||
'IABT': 0x20,
|
|
||||||
'IABT_HYP': 0x21,
|
|
||||||
'PC_ALIGN': 0x22,
|
|
||||||
'DABT': 0x24,
|
|
||||||
'DABT_HYP': 0x25,
|
|
||||||
'SP_ALIGN': 0x26,
|
|
||||||
'FP_EXC32': 0x28,
|
|
||||||
'FP_EXC64': 0x2C,
|
|
||||||
'SERROR': 0x2F,
|
|
||||||
'BREAKPT': 0x30,
|
|
||||||
'BREAKPT_HYP': 0x31,
|
|
||||||
'SOFTSTP': 0x32,
|
|
||||||
'SOFTSTP_HYP': 0x33,
|
|
||||||
'WATCHPT': 0x34,
|
|
||||||
'WATCHPT_HYP': 0x35,
|
|
||||||
'BKPT32': 0x38,
|
|
||||||
'VECTOR32': 0x3A,
|
|
||||||
'BRK64': 0x3C,
|
|
||||||
}
|
|
||||||
|
|
||||||
# From include/uapi/linux/kvm.h, KVM_EXIT_xxx
|
|
||||||
USERSPACE_EXIT_REASONS = {
|
|
||||||
'UNKNOWN': 0,
|
|
||||||
'EXCEPTION': 1,
|
|
||||||
'IO': 2,
|
|
||||||
'HYPERCALL': 3,
|
|
||||||
'DEBUG': 4,
|
|
||||||
'HLT': 5,
|
|
||||||
'MMIO': 6,
|
|
||||||
'IRQ_WINDOW_OPEN': 7,
|
|
||||||
'SHUTDOWN': 8,
|
|
||||||
'FAIL_ENTRY': 9,
|
|
||||||
'INTR': 10,
|
|
||||||
'SET_TPR': 11,
|
|
||||||
'TPR_ACCESS': 12,
|
|
||||||
'S390_SIEIC': 13,
|
|
||||||
'S390_RESET': 14,
|
|
||||||
'DCR': 15,
|
|
||||||
'NMI': 16,
|
|
||||||
'INTERNAL_ERROR': 17,
|
|
||||||
'OSI': 18,
|
|
||||||
'PAPR_HCALL': 19,
|
|
||||||
'S390_UCONTROL': 20,
|
|
||||||
'WATCHDOG': 21,
|
|
||||||
'S390_TSCH': 22,
|
|
||||||
'EPR': 23,
|
|
||||||
'SYSTEM_EVENT': 24,
|
|
||||||
}
|
|
||||||
|
|
||||||
IOCTL_NUMBERS = {
|
|
||||||
'SET_FILTER': 0x40082406,
|
|
||||||
'ENABLE': 0x00002400,
|
|
||||||
'DISABLE': 0x00002401,
|
|
||||||
'RESET': 0x00002403,
|
|
||||||
}
|
|
||||||
|
|
||||||
class Arch(object):
|
|
||||||
"""Class that encapsulates global architecture specific data like
|
|
||||||
syscall and ioctl numbers.
|
|
||||||
|
|
||||||
"""
|
|
||||||
@staticmethod
|
|
||||||
def get_arch():
|
|
||||||
machine = os.uname()[4]
|
|
||||||
|
|
||||||
if machine.startswith('ppc'):
|
|
||||||
return ArchPPC()
|
|
||||||
elif machine.startswith('aarch64'):
|
|
||||||
return ArchA64()
|
|
||||||
elif machine.startswith('s390'):
|
|
||||||
return ArchS390()
|
|
||||||
else:
|
|
||||||
# X86_64
|
|
||||||
for line in open('/proc/cpuinfo'):
|
|
||||||
if not line.startswith('flags'):
|
|
||||||
continue
|
|
||||||
|
|
||||||
flags = line.split()
|
|
||||||
if 'vmx' in flags:
|
|
||||||
return ArchX86(VMX_EXIT_REASONS)
|
|
||||||
if 'svm' in flags:
|
|
||||||
return ArchX86(SVM_EXIT_REASONS)
|
|
||||||
return
|
|
||||||
|
|
||||||
class ArchX86(Arch):
|
|
||||||
def __init__(self, exit_reasons):
|
|
||||||
self.sc_perf_evt_open = 298
|
|
||||||
self.ioctl_numbers = IOCTL_NUMBERS
|
|
||||||
self.exit_reasons = exit_reasons
|
|
||||||
|
|
||||||
class ArchPPC(Arch):
|
|
||||||
def __init__(self):
|
|
||||||
self.sc_perf_evt_open = 319
|
|
||||||
self.ioctl_numbers = IOCTL_NUMBERS
|
|
||||||
self.ioctl_numbers['ENABLE'] = 0x20002400
|
|
||||||
self.ioctl_numbers['DISABLE'] = 0x20002401
|
|
||||||
|
|
||||||
# PPC comes in 32 and 64 bit and some generated ioctl
|
|
||||||
# numbers depend on the wordsize.
|
|
||||||
char_ptr_size = ctypes.sizeof(ctypes.c_char_p)
|
|
||||||
self.ioctl_numbers['SET_FILTER'] = 0x80002406 | char_ptr_size << 16
|
|
||||||
|
|
||||||
class ArchA64(Arch):
|
|
||||||
def __init__(self):
|
|
||||||
self.sc_perf_evt_open = 241
|
|
||||||
self.ioctl_numbers = IOCTL_NUMBERS
|
|
||||||
self.exit_reasons = AARCH64_EXIT_REASONS
|
|
||||||
|
|
||||||
class ArchS390(Arch):
|
|
||||||
def __init__(self):
|
|
||||||
self.sc_perf_evt_open = 331
|
|
||||||
self.ioctl_numbers = IOCTL_NUMBERS
|
|
||||||
self.exit_reasons = None
|
|
||||||
|
|
||||||
ARCH = Arch.get_arch()
|
|
||||||
|
|
||||||
|
|
||||||
def walkdir(path):
|
|
||||||
"""Returns os.walk() data for specified directory.
|
|
||||||
|
|
||||||
As it is only a wrapper it returns the same 3-tuple of (dirpath,
|
|
||||||
dirnames, filenames).
|
|
||||||
"""
|
|
||||||
return next(os.walk(path))
|
|
||||||
|
|
||||||
|
|
||||||
def parse_int_list(list_string):
|
|
||||||
"""Returns an int list from a string of comma separated integers and
|
|
||||||
integer ranges."""
|
|
||||||
integers = []
|
|
||||||
members = list_string.split(',')
|
|
||||||
|
|
||||||
for member in members:
|
|
||||||
if '-' not in member:
|
|
||||||
integers.append(int(member))
|
|
||||||
else:
|
|
||||||
int_range = member.split('-')
|
|
||||||
integers.extend(range(int(int_range[0]),
|
|
||||||
int(int_range[1]) + 1))
|
|
||||||
|
|
||||||
return integers
|
|
||||||
|
|
||||||
|
|
||||||
def get_online_cpus():
|
|
||||||
with open('/sys/devices/system/cpu/online') as cpu_list:
|
|
||||||
cpu_string = cpu_list.readline()
|
|
||||||
return parse_int_list(cpu_string)
|
|
||||||
|
|
||||||
|
|
||||||
def get_filters():
|
|
||||||
filters = {}
|
|
||||||
filters['kvm_userspace_exit'] = ('reason', USERSPACE_EXIT_REASONS)
|
|
||||||
if ARCH.exit_reasons:
|
|
||||||
filters['kvm_exit'] = ('exit_reason', ARCH.exit_reasons)
|
|
||||||
return filters
|
|
||||||
|
|
||||||
libc = ctypes.CDLL('libc.so.6', use_errno=True)
|
|
||||||
syscall = libc.syscall
|
|
||||||
|
|
||||||
class perf_event_attr(ctypes.Structure):
|
|
||||||
_fields_ = [('type', ctypes.c_uint32),
|
|
||||||
('size', ctypes.c_uint32),
|
|
||||||
('config', ctypes.c_uint64),
|
|
||||||
('sample_freq', ctypes.c_uint64),
|
|
||||||
('sample_type', ctypes.c_uint64),
|
|
||||||
('read_format', ctypes.c_uint64),
|
|
||||||
('flags', ctypes.c_uint64),
|
|
||||||
('wakeup_events', ctypes.c_uint32),
|
|
||||||
('bp_type', ctypes.c_uint32),
|
|
||||||
('bp_addr', ctypes.c_uint64),
|
|
||||||
('bp_len', ctypes.c_uint64),
|
|
||||||
]
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
super(self.__class__, self).__init__()
|
|
||||||
self.type = PERF_TYPE_TRACEPOINT
|
|
||||||
self.size = ctypes.sizeof(self)
|
|
||||||
self.read_format = PERF_FORMAT_GROUP
|
|
||||||
|
|
||||||
def perf_event_open(attr, pid, cpu, group_fd, flags):
|
|
||||||
return syscall(ARCH.sc_perf_evt_open, ctypes.pointer(attr),
|
|
||||||
ctypes.c_int(pid), ctypes.c_int(cpu),
|
|
||||||
ctypes.c_int(group_fd), ctypes.c_long(flags))
|
|
||||||
|
|
||||||
PERF_TYPE_TRACEPOINT = 2
|
|
||||||
PERF_FORMAT_GROUP = 1 << 3
|
|
||||||
|
|
||||||
PATH_DEBUGFS_TRACING = '/sys/kernel/debug/tracing'
|
|
||||||
PATH_DEBUGFS_KVM = '/sys/kernel/debug/kvm'
|
|
||||||
|
|
||||||
class Group(object):
|
|
||||||
def __init__(self):
|
|
||||||
self.events = []
|
|
||||||
|
|
||||||
def add_event(self, event):
|
|
||||||
self.events.append(event)
|
|
||||||
|
|
||||||
def read(self):
|
|
||||||
length = 8 * (1 + len(self.events))
|
|
||||||
read_format = 'xxxxxxxx' + 'Q' * len(self.events)
|
|
||||||
return dict(zip([event.name for event in self.events],
|
|
||||||
struct.unpack(read_format,
|
|
||||||
os.read(self.events[0].fd, length))))
|
|
||||||
|
|
||||||
class Event(object):
|
|
||||||
def __init__(self, name, group, trace_cpu, trace_point, trace_filter,
|
|
||||||
trace_set='kvm'):
|
|
||||||
self.name = name
|
|
||||||
self.fd = None
|
|
||||||
self.setup_event(group, trace_cpu, trace_point, trace_filter,
|
|
||||||
trace_set)
|
|
||||||
|
|
||||||
def setup_event_attribute(self, trace_set, trace_point):
|
|
||||||
id_path = os.path.join(PATH_DEBUGFS_TRACING, 'events', trace_set,
|
|
||||||
trace_point, 'id')
|
|
||||||
|
|
||||||
event_attr = perf_event_attr()
|
|
||||||
event_attr.config = int(open(id_path).read())
|
|
||||||
return event_attr
|
|
||||||
|
|
||||||
def setup_event(self, group, trace_cpu, trace_point, trace_filter,
|
|
||||||
trace_set):
|
|
||||||
event_attr = self.setup_event_attribute(trace_set, trace_point)
|
|
||||||
|
|
||||||
group_leader = -1
|
|
||||||
if group.events:
|
|
||||||
group_leader = group.events[0].fd
|
|
||||||
|
|
||||||
fd = perf_event_open(event_attr, -1, trace_cpu,
|
|
||||||
group_leader, 0)
|
|
||||||
if fd == -1:
|
|
||||||
err = ctypes.get_errno()
|
|
||||||
raise OSError(err, os.strerror(err),
|
|
||||||
'while calling sys_perf_event_open().')
|
|
||||||
|
|
||||||
if trace_filter:
|
|
||||||
fcntl.ioctl(fd, ARCH.ioctl_numbers['SET_FILTER'],
|
|
||||||
trace_filter)
|
|
||||||
|
|
||||||
self.fd = fd
|
|
||||||
|
|
||||||
def enable(self):
|
|
||||||
fcntl.ioctl(self.fd, ARCH.ioctl_numbers['ENABLE'], 0)
|
|
||||||
|
|
||||||
def disable(self):
|
|
||||||
fcntl.ioctl(self.fd, ARCH.ioctl_numbers['DISABLE'], 0)
|
|
||||||
|
|
||||||
def reset(self):
|
|
||||||
fcntl.ioctl(self.fd, ARCH.ioctl_numbers['RESET'], 0)
|
|
||||||
|
|
||||||
class TracepointProvider(object):
|
|
||||||
def __init__(self):
|
|
||||||
self.group_leaders = []
|
|
||||||
self.filters = get_filters()
|
|
||||||
self._fields = self.get_available_fields()
|
|
||||||
self.setup_traces()
|
|
||||||
self.fields = self._fields
|
|
||||||
|
|
||||||
def get_available_fields(self):
|
|
||||||
path = os.path.join(PATH_DEBUGFS_TRACING, 'events', 'kvm')
|
|
||||||
fields = walkdir(path)[1]
|
|
||||||
extra = []
|
|
||||||
for field in fields:
|
|
||||||
if field in self.filters:
|
|
||||||
filter_name_, filter_dicts = self.filters[field]
|
|
||||||
for name in filter_dicts:
|
|
||||||
extra.append(field + '(' + name + ')')
|
|
||||||
fields += extra
|
|
||||||
return fields
|
|
||||||
|
|
||||||
def setup_traces(self):
|
|
||||||
cpus = get_online_cpus()
|
|
||||||
|
|
||||||
# The constant is needed as a buffer for python libs, std
|
|
||||||
# streams and other files that the script opens.
|
|
||||||
newlim = len(cpus) * len(self._fields) + 50
|
|
||||||
try:
|
|
||||||
softlim_, hardlim = resource.getrlimit(resource.RLIMIT_NOFILE)
|
|
||||||
|
|
||||||
if hardlim < newlim:
|
|
||||||
# Now we need CAP_SYS_RESOURCE, to increase the hard limit.
|
|
||||||
resource.setrlimit(resource.RLIMIT_NOFILE, (newlim, newlim))
|
|
||||||
else:
|
|
||||||
# Raising the soft limit is sufficient.
|
|
||||||
resource.setrlimit(resource.RLIMIT_NOFILE, (newlim, hardlim))
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
sys.exit("NOFILE rlimit could not be raised to {0}".format(newlim))
|
|
||||||
|
|
||||||
for cpu in cpus:
|
|
||||||
group = Group()
|
|
||||||
for name in self._fields:
|
|
||||||
tracepoint = name
|
|
||||||
tracefilter = None
|
|
||||||
match = re.match(r'(.*)\((.*)\)', name)
|
|
||||||
if match:
|
|
||||||
tracepoint, sub = match.groups()
|
|
||||||
tracefilter = ('%s==%d\0' %
|
|
||||||
(self.filters[tracepoint][0],
|
|
||||||
self.filters[tracepoint][1][sub]))
|
|
||||||
|
|
||||||
group.add_event(Event(name=name,
|
|
||||||
group=group,
|
|
||||||
trace_cpu=cpu,
|
|
||||||
trace_point=tracepoint,
|
|
||||||
trace_filter=tracefilter))
|
|
||||||
self.group_leaders.append(group)
|
|
||||||
|
|
||||||
def available_fields(self):
|
|
||||||
return self.get_available_fields()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def fields(self):
|
|
||||||
return self._fields
|
|
||||||
|
|
||||||
@fields.setter
|
|
||||||
def fields(self, fields):
|
|
||||||
self._fields = fields
|
|
||||||
for group in self.group_leaders:
|
|
||||||
for index, event in enumerate(group.events):
|
|
||||||
if event.name in fields:
|
|
||||||
event.reset()
|
|
||||||
event.enable()
|
|
||||||
else:
|
|
||||||
# Do not disable the group leader.
|
|
||||||
# It would disable all of its events.
|
|
||||||
if index != 0:
|
|
||||||
event.disable()
|
|
||||||
|
|
||||||
def read(self):
|
|
||||||
ret = defaultdict(int)
|
|
||||||
for group in self.group_leaders:
|
|
||||||
for name, val in group.read().iteritems():
|
|
||||||
if name in self._fields:
|
|
||||||
ret[name] += val
|
|
||||||
return ret
|
|
||||||
|
|
||||||
class DebugfsProvider(object):
|
|
||||||
def __init__(self):
|
|
||||||
self._fields = self.get_available_fields()
|
|
||||||
|
|
||||||
def get_available_fields(self):
|
|
||||||
return walkdir(PATH_DEBUGFS_KVM)[2]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def fields(self):
|
|
||||||
return self._fields
|
|
||||||
|
|
||||||
@fields.setter
|
|
||||||
def fields(self, fields):
|
|
||||||
self._fields = fields
|
|
||||||
|
|
||||||
def read(self):
|
|
||||||
def val(key):
|
|
||||||
return int(file(PATH_DEBUGFS_KVM + '/' + key).read())
|
|
||||||
return dict([(key, val(key)) for key in self._fields])
|
|
||||||
|
|
||||||
class Stats(object):
|
|
||||||
def __init__(self, providers, fields=None):
|
|
||||||
self.providers = providers
|
|
||||||
self._fields_filter = fields
|
|
||||||
self.values = {}
|
|
||||||
self.update_provider_filters()
|
|
||||||
|
|
||||||
def update_provider_filters(self):
|
|
||||||
def wanted(key):
|
|
||||||
if not self._fields_filter:
|
|
||||||
return True
|
|
||||||
return re.match(self._fields_filter, key) is not None
|
|
||||||
|
|
||||||
# As we reset the counters when updating the fields we can
|
|
||||||
# also clear the cache of old values.
|
|
||||||
self.values = {}
|
|
||||||
for provider in self.providers:
|
|
||||||
provider_fields = [key for key in provider.get_available_fields()
|
|
||||||
if wanted(key)]
|
|
||||||
provider.fields = provider_fields
|
|
||||||
|
|
||||||
@property
|
|
||||||
def fields_filter(self):
|
|
||||||
return self._fields_filter
|
|
||||||
|
|
||||||
@fields_filter.setter
|
|
||||||
def fields_filter(self, fields_filter):
|
|
||||||
self._fields_filter = fields_filter
|
|
||||||
self.update_provider_filters()
|
|
||||||
|
|
||||||
def get(self):
|
|
||||||
for provider in self.providers:
|
|
||||||
new = provider.read()
|
|
||||||
for key in provider.fields:
|
|
||||||
oldval = self.values.get(key, (0, 0))
|
|
||||||
newval = new.get(key, 0)
|
|
||||||
newdelta = None
|
|
||||||
if oldval is not None:
|
|
||||||
newdelta = newval - oldval[0]
|
|
||||||
self.values[key] = (newval, newdelta)
|
|
||||||
return self.values
|
|
||||||
|
|
||||||
LABEL_WIDTH = 40
|
|
||||||
NUMBER_WIDTH = 10
|
|
||||||
|
|
||||||
class Tui(object):
|
|
||||||
def __init__(self, stats):
|
|
||||||
self.stats = stats
|
|
||||||
self.screen = None
|
|
||||||
self.drilldown = False
|
|
||||||
self.update_drilldown()
|
|
||||||
|
|
||||||
def __enter__(self):
|
|
||||||
"""Initialises curses for later use. Based on curses.wrapper
|
|
||||||
implementation from the Python standard library."""
|
|
||||||
self.screen = curses.initscr()
|
|
||||||
curses.noecho()
|
|
||||||
curses.cbreak()
|
|
||||||
|
|
||||||
# The try/catch works around a minor bit of
|
|
||||||
# over-conscientiousness in the curses module, the error
|
|
||||||
# return from C start_color() is ignorable.
|
|
||||||
try:
|
|
||||||
curses.start_color()
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
curses.use_default_colors()
|
|
||||||
return self
|
|
||||||
|
|
||||||
def __exit__(self, *exception):
|
|
||||||
"""Resets the terminal to its normal state. Based on curses.wrappre
|
|
||||||
implementation from the Python standard library."""
|
|
||||||
if self.screen:
|
|
||||||
self.screen.keypad(0)
|
|
||||||
curses.echo()
|
|
||||||
curses.nocbreak()
|
|
||||||
curses.endwin()
|
|
||||||
|
|
||||||
def update_drilldown(self):
|
|
||||||
if not self.stats.fields_filter:
|
|
||||||
self.stats.fields_filter = r'^[^\(]*$'
|
|
||||||
|
|
||||||
elif self.stats.fields_filter == r'^[^\(]*$':
|
|
||||||
self.stats.fields_filter = None
|
|
||||||
|
|
||||||
def refresh(self, sleeptime):
|
|
||||||
self.screen.erase()
|
|
||||||
self.screen.addstr(0, 0, 'kvm statistics - summary', curses.A_BOLD)
|
|
||||||
self.screen.addstr(2, 1, 'Event')
|
|
||||||
self.screen.addstr(2, 1 + LABEL_WIDTH + NUMBER_WIDTH -
|
|
||||||
len('Total'), 'Total')
|
|
||||||
self.screen.addstr(2, 1 + LABEL_WIDTH + NUMBER_WIDTH + 8 -
|
|
||||||
len('Current'), 'Current')
|
|
||||||
row = 3
|
|
||||||
stats = self.stats.get()
|
|
||||||
def sortkey(x):
|
|
||||||
if stats[x][1]:
|
|
||||||
return (-stats[x][1], -stats[x][0])
|
|
||||||
else:
|
|
||||||
return (0, -stats[x][0])
|
|
||||||
for key in sorted(stats.keys(), key=sortkey):
|
|
||||||
|
|
||||||
if row >= self.screen.getmaxyx()[0]:
|
|
||||||
break
|
|
||||||
values = stats[key]
|
|
||||||
if not values[0] and not values[1]:
|
|
||||||
break
|
|
||||||
col = 1
|
|
||||||
self.screen.addstr(row, col, key)
|
|
||||||
col += LABEL_WIDTH
|
|
||||||
self.screen.addstr(row, col, '%10d' % (values[0],))
|
|
||||||
col += NUMBER_WIDTH
|
|
||||||
if values[1] is not None:
|
|
||||||
self.screen.addstr(row, col, '%8d' % (values[1] / sleeptime,))
|
|
||||||
row += 1
|
|
||||||
self.screen.refresh()
|
|
||||||
|
|
||||||
def show_filter_selection(self):
|
|
||||||
while True:
|
|
||||||
self.screen.erase()
|
|
||||||
self.screen.addstr(0, 0,
|
|
||||||
"Show statistics for events matching a regex.",
|
|
||||||
curses.A_BOLD)
|
|
||||||
self.screen.addstr(2, 0,
|
|
||||||
"Current regex: {0}"
|
|
||||||
.format(self.stats.fields_filter))
|
|
||||||
self.screen.addstr(3, 0, "New regex: ")
|
|
||||||
curses.echo()
|
|
||||||
regex = self.screen.getstr()
|
|
||||||
curses.noecho()
|
|
||||||
if len(regex) == 0:
|
|
||||||
return
|
|
||||||
try:
|
|
||||||
re.compile(regex)
|
|
||||||
self.stats.fields_filter = regex
|
|
||||||
return
|
|
||||||
except re.error:
|
|
||||||
continue
|
|
||||||
|
|
||||||
def show_stats(self):
|
|
||||||
sleeptime = 0.25
|
|
||||||
while True:
|
|
||||||
self.refresh(sleeptime)
|
|
||||||
curses.halfdelay(int(sleeptime * 10))
|
|
||||||
sleeptime = 3
|
|
||||||
try:
|
|
||||||
char = self.screen.getkey()
|
|
||||||
if char == 'x':
|
|
||||||
self.drilldown = not self.drilldown
|
|
||||||
self.update_drilldown()
|
|
||||||
if char == 'q':
|
|
||||||
break
|
|
||||||
if char == 'f':
|
|
||||||
self.show_filter_selection()
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
break
|
|
||||||
except curses.error:
|
|
||||||
continue
|
|
||||||
|
|
||||||
def batch(stats):
|
|
||||||
s = stats.get()
|
|
||||||
time.sleep(1)
|
|
||||||
s = stats.get()
|
|
||||||
for key in sorted(s.keys()):
|
|
||||||
values = s[key]
|
|
||||||
print '%-42s%10d%10d' % (key, values[0], values[1])
|
|
||||||
|
|
||||||
def log(stats):
|
|
||||||
keys = sorted(stats.get().iterkeys())
|
|
||||||
def banner():
|
|
||||||
for k in keys:
|
|
||||||
print '%s' % k,
|
|
||||||
print
|
|
||||||
def statline():
|
|
||||||
s = stats.get()
|
|
||||||
for k in keys:
|
|
||||||
print ' %9d' % s[k][1],
|
|
||||||
print
|
|
||||||
line = 0
|
|
||||||
banner_repeat = 20
|
|
||||||
while True:
|
|
||||||
time.sleep(1)
|
|
||||||
if line % banner_repeat == 0:
|
|
||||||
banner()
|
|
||||||
statline()
|
|
||||||
line += 1
|
|
||||||
|
|
||||||
def get_options():
|
|
||||||
description_text = """
|
|
||||||
This script displays various statistics about VMs running under KVM.
|
|
||||||
The statistics are gathered from the KVM debugfs entries and / or the
|
|
||||||
currently available perf traces.
|
|
||||||
|
|
||||||
The monitoring takes additional cpu cycles and might affect the VM's
|
|
||||||
performance.
|
|
||||||
|
|
||||||
Requirements:
|
|
||||||
- Access to:
|
|
||||||
/sys/kernel/debug/kvm
|
|
||||||
/sys/kernel/debug/trace/events/*
|
|
||||||
/proc/pid/task
|
|
||||||
- /proc/sys/kernel/perf_event_paranoid < 1 if user has no
|
|
||||||
CAP_SYS_ADMIN and perf events are used.
|
|
||||||
- CAP_SYS_RESOURCE if the hard limit is not high enough to allow
|
|
||||||
the large number of files that are possibly opened.
|
|
||||||
"""
|
|
||||||
|
|
||||||
class PlainHelpFormatter(optparse.IndentedHelpFormatter):
|
|
||||||
def format_description(self, description):
|
|
||||||
if description:
|
|
||||||
return description + "\n"
|
|
||||||
else:
|
|
||||||
return ""
|
|
||||||
|
|
||||||
optparser = optparse.OptionParser(description=description_text,
|
|
||||||
formatter=PlainHelpFormatter())
|
|
||||||
optparser.add_option('-1', '--once', '--batch',
|
|
||||||
action='store_true',
|
|
||||||
default=False,
|
|
||||||
dest='once',
|
|
||||||
help='run in batch mode for one second',
|
|
||||||
)
|
|
||||||
optparser.add_option('-l', '--log',
|
|
||||||
action='store_true',
|
|
||||||
default=False,
|
|
||||||
dest='log',
|
|
||||||
help='run in logging mode (like vmstat)',
|
|
||||||
)
|
|
||||||
optparser.add_option('-t', '--tracepoints',
|
|
||||||
action='store_true',
|
|
||||||
default=False,
|
|
||||||
dest='tracepoints',
|
|
||||||
help='retrieve statistics from tracepoints',
|
|
||||||
)
|
|
||||||
optparser.add_option('-d', '--debugfs',
|
|
||||||
action='store_true',
|
|
||||||
default=False,
|
|
||||||
dest='debugfs',
|
|
||||||
help='retrieve statistics from debugfs',
|
|
||||||
)
|
|
||||||
optparser.add_option('-f', '--fields',
|
|
||||||
action='store',
|
|
||||||
default=None,
|
|
||||||
dest='fields',
|
|
||||||
help='fields to display (regex)',
|
|
||||||
)
|
|
||||||
(options, _) = optparser.parse_args(sys.argv)
|
|
||||||
return options
|
|
||||||
|
|
||||||
def get_providers(options):
|
|
||||||
providers = []
|
|
||||||
|
|
||||||
if options.tracepoints:
|
|
||||||
providers.append(TracepointProvider())
|
|
||||||
if options.debugfs:
|
|
||||||
providers.append(DebugfsProvider())
|
|
||||||
if len(providers) == 0:
|
|
||||||
providers.append(TracepointProvider())
|
|
||||||
|
|
||||||
return providers
|
|
||||||
|
|
||||||
def check_access(options):
|
|
||||||
if not os.path.exists('/sys/kernel/debug'):
|
|
||||||
sys.stderr.write('Please enable CONFIG_DEBUG_FS in your kernel.')
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
if not os.path.exists(PATH_DEBUGFS_KVM):
|
|
||||||
sys.stderr.write("Please make sure, that debugfs is mounted and "
|
|
||||||
"readable by the current user:\n"
|
|
||||||
"('mount -t debugfs debugfs /sys/kernel/debug')\n"
|
|
||||||
"Also ensure, that the kvm modules are loaded.\n")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
if not os.path.exists(PATH_DEBUGFS_TRACING) and (options.tracepoints
|
|
||||||
or not options.debugfs):
|
|
||||||
sys.stderr.write("Please enable CONFIG_TRACING in your kernel "
|
|
||||||
"when using the option -t (default).\n"
|
|
||||||
"If it is enabled, make {0} readable by the "
|
|
||||||
"current user.\n"
|
|
||||||
.format(PATH_DEBUGFS_TRACING))
|
|
||||||
if options.tracepoints:
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
sys.stderr.write("Falling back to debugfs statistics!\n")
|
|
||||||
options.debugfs = True
|
|
||||||
sleep(5)
|
|
||||||
|
|
||||||
return options
|
|
||||||
|
|
||||||
def main():
|
|
||||||
options = get_options()
|
|
||||||
options = check_access(options)
|
|
||||||
providers = get_providers(options)
|
|
||||||
stats = Stats(providers, fields=options.fields)
|
|
||||||
|
|
||||||
if options.log:
|
|
||||||
log(stats)
|
|
||||||
elif not options.once:
|
|
||||||
with Tui(stats) as tui:
|
|
||||||
tui.show_stats()
|
|
||||||
else:
|
|
||||||
batch(stats)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
|
@ -1,55 +0,0 @@
|
||||||
@example
|
|
||||||
@c man begin SYNOPSIS
|
|
||||||
usage: kvm_stat [OPTION]...
|
|
||||||
@c man end
|
|
||||||
@end example
|
|
||||||
|
|
||||||
@c man begin DESCRIPTION
|
|
||||||
|
|
||||||
kvm_stat prints counts of KVM kernel module trace events. These events signify
|
|
||||||
state transitions such as guest mode entry and exit.
|
|
||||||
|
|
||||||
This tool is useful for observing guest behavior from the host perspective.
|
|
||||||
Often conclusions about performance or buggy behavior can be drawn from the
|
|
||||||
output.
|
|
||||||
|
|
||||||
The set of KVM kernel module trace events may be specific to the kernel version
|
|
||||||
or architecture. It is best to check the KVM kernel module source code for the
|
|
||||||
meaning of events.
|
|
||||||
|
|
||||||
Note that trace events are counted globally across all running guests.
|
|
||||||
|
|
||||||
@c man end
|
|
||||||
|
|
||||||
@c man begin OPTIONS
|
|
||||||
@table @option
|
|
||||||
@item -1, --once, --batch
|
|
||||||
run in batch mode for one second
|
|
||||||
@item -l, --log
|
|
||||||
run in logging mode (like vmstat)
|
|
||||||
@item -t, --tracepoints
|
|
||||||
retrieve statistics from tracepoints
|
|
||||||
@item -d, --debugfs
|
|
||||||
retrieve statistics from debugfs
|
|
||||||
@item -f, --fields=@var{fields}
|
|
||||||
fields to display (regex)
|
|
||||||
@item -h, --help
|
|
||||||
show help message
|
|
||||||
@end table
|
|
||||||
|
|
||||||
@c man end
|
|
||||||
|
|
||||||
@ignore
|
|
||||||
|
|
||||||
@setfilename kvm_stat
|
|
||||||
@settitle Report KVM kernel module event counters.
|
|
||||||
|
|
||||||
@c man begin AUTHOR
|
|
||||||
Stefan Hajnoczi <stefanha@redhat.com>
|
|
||||||
@c man end
|
|
||||||
|
|
||||||
@c man begin SEEALSO
|
|
||||||
perf(1), trace-cmd(1)
|
|
||||||
@c man end
|
|
||||||
|
|
||||||
@end ignore
|
|
|
@ -411,7 +411,8 @@ int kvm_arch_on_sigbus_vcpu(CPUState *c, int code, void *addr)
|
||||||
|
|
||||||
if ((env->mcg_cap & MCG_SER_P) && addr
|
if ((env->mcg_cap & MCG_SER_P) && addr
|
||||||
&& (code == BUS_MCEERR_AR || code == BUS_MCEERR_AO)) {
|
&& (code == BUS_MCEERR_AR || code == BUS_MCEERR_AO)) {
|
||||||
if (qemu_ram_addr_from_host(addr, &ram_addr) == NULL ||
|
ram_addr = qemu_ram_addr_from_host(addr);
|
||||||
|
if (ram_addr == RAM_ADDR_INVALID ||
|
||||||
!kvm_physical_memory_addr_from_host(c->kvm_state, addr, &paddr)) {
|
!kvm_physical_memory_addr_from_host(c->kvm_state, addr, &paddr)) {
|
||||||
fprintf(stderr, "Hardware memory error for memory used by "
|
fprintf(stderr, "Hardware memory error for memory used by "
|
||||||
"QEMU itself instead of guest system!\n");
|
"QEMU itself instead of guest system!\n");
|
||||||
|
@ -445,7 +446,8 @@ int kvm_arch_on_sigbus(int code, void *addr)
|
||||||
hwaddr paddr;
|
hwaddr paddr;
|
||||||
|
|
||||||
/* Hope we are lucky for AO MCE */
|
/* Hope we are lucky for AO MCE */
|
||||||
if (qemu_ram_addr_from_host(addr, &ram_addr) == NULL ||
|
ram_addr = qemu_ram_addr_from_host(addr);
|
||||||
|
if (ram_addr == RAM_ADDR_INVALID ||
|
||||||
!kvm_physical_memory_addr_from_host(first_cpu->kvm_state,
|
!kvm_physical_memory_addr_from_host(first_cpu->kvm_state,
|
||||||
addr, &paddr)) {
|
addr, &paddr)) {
|
||||||
fprintf(stderr, "Hardware memory error for memory used by "
|
fprintf(stderr, "Hardware memory error for memory used by "
|
||||||
|
|
14
xen-hvm.c
14
xen-hvm.c
|
@ -511,8 +511,13 @@ static void xen_io_add(MemoryListener *listener,
|
||||||
MemoryRegionSection *section)
|
MemoryRegionSection *section)
|
||||||
{
|
{
|
||||||
XenIOState *state = container_of(listener, XenIOState, io_listener);
|
XenIOState *state = container_of(listener, XenIOState, io_listener);
|
||||||
|
MemoryRegion *mr = section->mr;
|
||||||
|
|
||||||
memory_region_ref(section->mr);
|
if (mr->ops == &unassigned_io_ops) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memory_region_ref(mr);
|
||||||
|
|
||||||
xen_map_io_section(xen_xc, xen_domid, state->ioservid, section);
|
xen_map_io_section(xen_xc, xen_domid, state->ioservid, section);
|
||||||
}
|
}
|
||||||
|
@ -521,10 +526,15 @@ static void xen_io_del(MemoryListener *listener,
|
||||||
MemoryRegionSection *section)
|
MemoryRegionSection *section)
|
||||||
{
|
{
|
||||||
XenIOState *state = container_of(listener, XenIOState, io_listener);
|
XenIOState *state = container_of(listener, XenIOState, io_listener);
|
||||||
|
MemoryRegion *mr = section->mr;
|
||||||
|
|
||||||
|
if (mr->ops == &unassigned_io_ops) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
xen_unmap_io_section(xen_xc, xen_domid, state->ioservid, section);
|
xen_unmap_io_section(xen_xc, xen_domid, state->ioservid, section);
|
||||||
|
|
||||||
memory_region_unref(section->mr);
|
memory_region_unref(mr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void xen_device_realize(DeviceListener *listener,
|
static void xen_device_realize(DeviceListener *listener,
|
||||||
|
|
Loading…
Reference in New Issue