Pull request

-----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEGJn/jt6/WMzuA0uC9IfvGFhy1yMFAl0shHUACgkQ9IfvGFhy
 1yN2ThAAgo0o75GcC3p2pYM+jhZR+YpH5vffOa82bKRBS/toxzobXLyiD1jB627T
 oTr7WMWNxt0Tc6m4NEgnHCrFhu59aSELKWj/SY7gpjgUgtZBwU+BMEs6VPwXDpWz
 bXxNnp5O27pYLfYmN8p24cV9VgZV9e1J75H1w4zjJD4dN+T+f2+QZcxLZOVXe5v+
 WnUgY9EGg9ruXb72MRZhhh/OgXKLOaj0nKQ8OgtkADbs0hj6mNpwxt2YxwOZhnYu
 drnVCEl/mS3kCFCpyyeRxYtgPJ+ykXVykkUEHNZLNZ96Tck379+gUhOLg0/wf1vn
 7R/h22XzQtkoDkUfVn0XsGbsOLr//wljoYafl8cok77BFSfMVkOP1KpQvdzYhHWn
 4Tl0cX4wk0qoDKX/T78+7RTq86G9KwDlg8wA0vXloVd+V3RBg3kWUVOMhIYxtOYX
 QbzNuR+rsSY2vx0ciGVuCRfynsb2Di66KiEtfSMv3pGjdz3g5CmlJr6sgI0no4EE
 prKdxifiP+2CJ7U9ffGbIlLY4dk+YWibYwmCxGDQL/hKWLIf2xUbpGszxlgGfTjB
 QPMh8FGLGcz9yraB9u0N2KaJX7baLMfIEztCj+xH3EsgCT2PjSO+Pooyg29waBUZ
 cs1Eb7RBZZCmJKTuvLjTyz0iCD6f1NMqu9VZOZmdcJxLd1QtZI8=
 =Gsak
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/juanquintela/tags/migration-pull-request' into staging

Pull request

# gpg: Signature made Mon 15 Jul 2019 14:49:41 BST
# gpg:                using RSA key 1899FF8EDEBF58CCEE034B82F487EF185872D723
# gpg: Good signature from "Juan Quintela <quintela@redhat.com>" [full]
# gpg:                 aka "Juan Quintela <quintela@trasno.org>" [full]
# Primary key fingerprint: 1899 FF8E DEBF 58CC EE03  4B82 F487 EF18 5872 D723

* remotes/juanquintela/tags/migration-pull-request: (21 commits)
  migration: always initial RAMBlock.bmap to 1 for new migration
  migration/postcopy: remove redundant cpu_synchronize_all_post_init
  migration/postcopy: fix document of postcopy_send_discard_bm_ram()
  migration: allow private destination ram with x-ignore-shared
  migration: Split log_clear() into smaller chunks
  kvm: Support KVM_CLEAR_DIRTY_LOG
  kvm: Introduce slots lock for memory listener
  kvm: Persistent per kvmslot dirty bitmap
  kvm: Update comments for sync_dirty_bitmap
  memory: Introduce memory listener hook log_clear()
  memory: Pass mr into snapshot_and_clear_dirty
  bitmap: Add bitmap_copy_with_{src|dst}_offset()
  memory: Don't set migration bitmap when without migration
  migration: No need to take rcu during sync_dirty_bitmap
  migration/ram.c: reset complete_round when we gets a queued page
  migration/multifd: sync packet_num after all thread are done
  cutils: remove one unnecessary pointer operation
  migration/xbzrle: update cache and current_data in one place
  migration/multifd: call multifd_send_sync_main when sending RAM_SAVE_FLAG_EOS
  migration-test: rename parameter to parameter_int
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2019-07-15 15:19:54 +01:00
commit a68725f930
18 changed files with 732 additions and 94 deletions

View File

@ -91,6 +91,7 @@ struct KVMState
int many_ioeventfds; int many_ioeventfds;
int intx_set_mask; int intx_set_mask;
bool sync_mmu; bool sync_mmu;
bool manual_dirty_log_protect;
/* The man page (and posix) say ioctl numbers are signed int, but /* The man page (and posix) say ioctl numbers are signed int, but
* they're not. Linux, glibc and *BSD all treat ioctl numbers as * they're not. Linux, glibc and *BSD all treat ioctl numbers as
* unsigned, and treating them as signed here can break things */ * unsigned, and treating them as signed here can break things */
@ -138,6 +139,9 @@ static const KVMCapabilityInfo kvm_required_capabilites[] = {
KVM_CAP_LAST_INFO KVM_CAP_LAST_INFO
}; };
#define kvm_slots_lock(kml) qemu_mutex_lock(&(kml)->slots_lock)
#define kvm_slots_unlock(kml) qemu_mutex_unlock(&(kml)->slots_lock)
int kvm_get_max_memslots(void) int kvm_get_max_memslots(void)
{ {
KVMState *s = KVM_STATE(current_machine->accelerator); KVMState *s = KVM_STATE(current_machine->accelerator);
@ -165,6 +169,7 @@ int kvm_memcrypt_encrypt_data(uint8_t *ptr, uint64_t len)
return 1; return 1;
} }
/* Called with KVMMemoryListener.slots_lock held */
static KVMSlot *kvm_get_free_slot(KVMMemoryListener *kml) static KVMSlot *kvm_get_free_slot(KVMMemoryListener *kml)
{ {
KVMState *s = kvm_state; KVMState *s = kvm_state;
@ -182,10 +187,17 @@ static KVMSlot *kvm_get_free_slot(KVMMemoryListener *kml)
bool kvm_has_free_slot(MachineState *ms) bool kvm_has_free_slot(MachineState *ms)
{ {
KVMState *s = KVM_STATE(ms->accelerator); KVMState *s = KVM_STATE(ms->accelerator);
bool result;
KVMMemoryListener *kml = &s->memory_listener;
return kvm_get_free_slot(&s->memory_listener); kvm_slots_lock(kml);
result = !!kvm_get_free_slot(kml);
kvm_slots_unlock(kml);
return result;
} }
/* Called with KVMMemoryListener.slots_lock held */
static KVMSlot *kvm_alloc_slot(KVMMemoryListener *kml) static KVMSlot *kvm_alloc_slot(KVMMemoryListener *kml)
{ {
KVMSlot *slot = kvm_get_free_slot(kml); KVMSlot *slot = kvm_get_free_slot(kml);
@ -244,18 +256,21 @@ int kvm_physical_memory_addr_from_host(KVMState *s, void *ram,
hwaddr *phys_addr) hwaddr *phys_addr)
{ {
KVMMemoryListener *kml = &s->memory_listener; KVMMemoryListener *kml = &s->memory_listener;
int i; int i, ret = 0;
kvm_slots_lock(kml);
for (i = 0; i < s->nr_slots; i++) { for (i = 0; i < s->nr_slots; i++) {
KVMSlot *mem = &kml->slots[i]; KVMSlot *mem = &kml->slots[i];
if (ram >= mem->ram && ram < mem->ram + mem->memory_size) { if (ram >= mem->ram && ram < mem->ram + mem->memory_size) {
*phys_addr = mem->start_addr + (ram - mem->ram); *phys_addr = mem->start_addr + (ram - mem->ram);
return 1; ret = 1;
break;
} }
} }
kvm_slots_unlock(kml);
return 0; return ret;
} }
static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, bool new) static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, bool new)
@ -396,6 +411,7 @@ static int kvm_mem_flags(MemoryRegion *mr)
return flags; return flags;
} }
/* Called with KVMMemoryListener.slots_lock held */
static int kvm_slot_update_flags(KVMMemoryListener *kml, KVMSlot *mem, static int kvm_slot_update_flags(KVMMemoryListener *kml, KVMSlot *mem,
MemoryRegion *mr) MemoryRegion *mr)
{ {
@ -414,19 +430,26 @@ static int kvm_section_update_flags(KVMMemoryListener *kml,
{ {
hwaddr start_addr, size; hwaddr start_addr, size;
KVMSlot *mem; KVMSlot *mem;
int ret = 0;
size = kvm_align_section(section, &start_addr); size = kvm_align_section(section, &start_addr);
if (!size) { if (!size) {
return 0; return 0;
} }
kvm_slots_lock(kml);
mem = kvm_lookup_matching_slot(kml, start_addr, size); mem = kvm_lookup_matching_slot(kml, start_addr, size);
if (!mem) { if (!mem) {
/* We don't have a slot if we want to trap every access. */ /* We don't have a slot if we want to trap every access. */
return 0; goto out;
} }
return kvm_slot_update_flags(kml, mem, section->mr); ret = kvm_slot_update_flags(kml, mem, section->mr);
out:
kvm_slots_unlock(kml);
return ret;
} }
static void kvm_log_start(MemoryListener *listener, static void kvm_log_start(MemoryListener *listener,
@ -478,13 +501,15 @@ static int kvm_get_dirty_pages_log_range(MemoryRegionSection *section,
#define ALIGN(x, y) (((x)+(y)-1) & ~((y)-1)) #define ALIGN(x, y) (((x)+(y)-1) & ~((y)-1))
/** /**
* kvm_physical_sync_dirty_bitmap - Grab dirty bitmap from kernel space * kvm_physical_sync_dirty_bitmap - Sync dirty bitmap from kernel space
* This function updates qemu's dirty bitmap using
* memory_region_set_dirty(). This means all bits are set
* to dirty.
* *
* @start_add: start of logged region. * This function will first try to fetch dirty bitmap from the kernel,
* @end_addr: end of logged region. * and then updates qemu's dirty bitmap.
*
* NOTE: caller must be with kml->slots_lock held.
*
* @kml: the KVM memory listener object
* @section: the memory section to sync the dirty bitmap with
*/ */
static int kvm_physical_sync_dirty_bitmap(KVMMemoryListener *kml, static int kvm_physical_sync_dirty_bitmap(KVMMemoryListener *kml,
MemoryRegionSection *section) MemoryRegionSection *section)
@ -493,13 +518,14 @@ static int kvm_physical_sync_dirty_bitmap(KVMMemoryListener *kml,
struct kvm_dirty_log d = {}; struct kvm_dirty_log d = {};
KVMSlot *mem; KVMSlot *mem;
hwaddr start_addr, size; hwaddr start_addr, size;
int ret = 0;
size = kvm_align_section(section, &start_addr); size = kvm_align_section(section, &start_addr);
if (size) { if (size) {
mem = kvm_lookup_matching_slot(kml, start_addr, size); mem = kvm_lookup_matching_slot(kml, start_addr, size);
if (!mem) { if (!mem) {
/* We don't have a slot if we want to trap every access. */ /* We don't have a slot if we want to trap every access. */
return 0; goto out;
} }
/* XXX bad kernel interface alert /* XXX bad kernel interface alert
@ -516,20 +542,176 @@ static int kvm_physical_sync_dirty_bitmap(KVMMemoryListener *kml,
*/ */
size = ALIGN(((mem->memory_size) >> TARGET_PAGE_BITS), size = ALIGN(((mem->memory_size) >> TARGET_PAGE_BITS),
/*HOST_LONG_BITS*/ 64) / 8; /*HOST_LONG_BITS*/ 64) / 8;
d.dirty_bitmap = g_malloc0(size); if (!mem->dirty_bmap) {
/* Allocate on the first log_sync, once and for all */
mem->dirty_bmap = g_malloc0(size);
}
d.dirty_bitmap = mem->dirty_bmap;
d.slot = mem->slot | (kml->as_id << 16); d.slot = mem->slot | (kml->as_id << 16);
if (kvm_vm_ioctl(s, KVM_GET_DIRTY_LOG, &d) == -1) { if (kvm_vm_ioctl(s, KVM_GET_DIRTY_LOG, &d) == -1) {
DPRINTF("ioctl failed %d\n", errno); DPRINTF("ioctl failed %d\n", errno);
g_free(d.dirty_bitmap); ret = -1;
return -1; goto out;
} }
kvm_get_dirty_pages_log_range(section, d.dirty_bitmap); kvm_get_dirty_pages_log_range(section, d.dirty_bitmap);
g_free(d.dirty_bitmap); }
out:
return ret;
}
/* Alignment requirement for KVM_CLEAR_DIRTY_LOG - 64 pages */
#define KVM_CLEAR_LOG_SHIFT 6
#define KVM_CLEAR_LOG_ALIGN (qemu_real_host_page_size << KVM_CLEAR_LOG_SHIFT)
#define KVM_CLEAR_LOG_MASK (-KVM_CLEAR_LOG_ALIGN)
/**
* kvm_physical_log_clear - Clear the kernel's dirty bitmap for range
*
* NOTE: this will be a no-op if we haven't enabled manual dirty log
* protection in the host kernel because in that case this operation
* will be done within log_sync().
*
* @kml: the kvm memory listener
* @section: the memory range to clear dirty bitmap
*/
static int kvm_physical_log_clear(KVMMemoryListener *kml,
MemoryRegionSection *section)
{
KVMState *s = kvm_state;
struct kvm_clear_dirty_log d;
uint64_t start, end, bmap_start, start_delta, bmap_npages, size;
unsigned long *bmap_clear = NULL, psize = qemu_real_host_page_size;
KVMSlot *mem = NULL;
int ret, i;
if (!s->manual_dirty_log_protect) {
/* No need to do explicit clear */
return 0;
} }
return 0; start = section->offset_within_address_space;
size = int128_get64(section->size);
if (!size) {
/* Nothing more we can do... */
return 0;
}
kvm_slots_lock(kml);
/* Find any possible slot that covers the section */
for (i = 0; i < s->nr_slots; i++) {
mem = &kml->slots[i];
if (mem->start_addr <= start &&
start + size <= mem->start_addr + mem->memory_size) {
break;
}
}
/*
* We should always find one memslot until this point, otherwise
* there could be something wrong from the upper layer
*/
assert(mem && i != s->nr_slots);
/*
* We need to extend either the start or the size or both to
* satisfy the KVM interface requirement. Firstly, do the start
* page alignment on 64 host pages
*/
bmap_start = (start - mem->start_addr) & KVM_CLEAR_LOG_MASK;
start_delta = start - mem->start_addr - bmap_start;
bmap_start /= psize;
/*
* The kernel interface has restriction on the size too, that either:
*
* (1) the size is 64 host pages aligned (just like the start), or
* (2) the size fills up until the end of the KVM memslot.
*/
bmap_npages = DIV_ROUND_UP(size + start_delta, KVM_CLEAR_LOG_ALIGN)
<< KVM_CLEAR_LOG_SHIFT;
end = mem->memory_size / psize;
if (bmap_npages > end - bmap_start) {
bmap_npages = end - bmap_start;
}
start_delta /= psize;
/*
* Prepare the bitmap to clear dirty bits. Here we must guarantee
* that we won't clear any unknown dirty bits otherwise we might
* accidentally clear some set bits which are not yet synced from
* the kernel into QEMU's bitmap, then we'll lose track of the
* guest modifications upon those pages (which can directly lead
* to guest data loss or panic after migration).
*
* Layout of the KVMSlot.dirty_bmap:
*
* |<-------- bmap_npages -----------..>|
* [1]
* start_delta size
* |----------------|-------------|------------------|------------|
* ^ ^ ^ ^
* | | | |
* start bmap_start (start) end
* of memslot of memslot
*
* [1] bmap_npages can be aligned to either 64 pages or the end of slot
*/
assert(bmap_start % BITS_PER_LONG == 0);
/* We should never do log_clear before log_sync */
assert(mem->dirty_bmap);
if (start_delta) {
/* Slow path - we need to manipulate a temp bitmap */
bmap_clear = bitmap_new(bmap_npages);
bitmap_copy_with_src_offset(bmap_clear, mem->dirty_bmap,
bmap_start, start_delta + size / psize);
/*
* We need to fill the holes at start because that was not
* specified by the caller and we extended the bitmap only for
* 64 pages alignment
*/
bitmap_clear(bmap_clear, 0, start_delta);
d.dirty_bitmap = bmap_clear;
} else {
/* Fast path - start address aligns well with BITS_PER_LONG */
d.dirty_bitmap = mem->dirty_bmap + BIT_WORD(bmap_start);
}
d.first_page = bmap_start;
/* It should never overflow. If it happens, say something */
assert(bmap_npages <= UINT32_MAX);
d.num_pages = bmap_npages;
d.slot = mem->slot | (kml->as_id << 16);
if (kvm_vm_ioctl(s, KVM_CLEAR_DIRTY_LOG, &d) == -1) {
ret = -errno;
error_report("%s: KVM_CLEAR_DIRTY_LOG failed, slot=%d, "
"start=0x%"PRIx64", size=0x%"PRIx32", errno=%d",
__func__, d.slot, (uint64_t)d.first_page,
(uint32_t)d.num_pages, ret);
} else {
ret = 0;
trace_kvm_clear_dirty_log(d.slot, d.first_page, d.num_pages);
}
/*
* After we have updated the remote dirty bitmap, we update the
* cached bitmap as well for the memslot, then if another user
* clears the same region we know we shouldn't clear it again on
* the remote otherwise it's data loss as well.
*/
bitmap_clear(mem->dirty_bmap, bmap_start + start_delta,
size / psize);
/* This handles the NULL case well */
g_free(bmap_clear);
kvm_slots_unlock(kml);
return ret;
} }
static void kvm_coalesce_mmio_region(MemoryListener *listener, static void kvm_coalesce_mmio_region(MemoryListener *listener,
@ -791,16 +973,20 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
ram = memory_region_get_ram_ptr(mr) + section->offset_within_region + ram = memory_region_get_ram_ptr(mr) + section->offset_within_region +
(start_addr - section->offset_within_address_space); (start_addr - section->offset_within_address_space);
kvm_slots_lock(kml);
if (!add) { if (!add) {
mem = kvm_lookup_matching_slot(kml, start_addr, size); mem = kvm_lookup_matching_slot(kml, start_addr, size);
if (!mem) { if (!mem) {
return; goto out;
} }
if (mem->flags & KVM_MEM_LOG_DIRTY_PAGES) { if (mem->flags & KVM_MEM_LOG_DIRTY_PAGES) {
kvm_physical_sync_dirty_bitmap(kml, section); kvm_physical_sync_dirty_bitmap(kml, section);
} }
/* unregister the slot */ /* unregister the slot */
g_free(mem->dirty_bmap);
mem->dirty_bmap = NULL;
mem->memory_size = 0; mem->memory_size = 0;
mem->flags = 0; mem->flags = 0;
err = kvm_set_user_memory_region(kml, mem, false); err = kvm_set_user_memory_region(kml, mem, false);
@ -809,7 +995,7 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
__func__, strerror(-err)); __func__, strerror(-err));
abort(); abort();
} }
return; goto out;
} }
/* register the new slot */ /* register the new slot */
@ -825,6 +1011,9 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
strerror(-err)); strerror(-err));
abort(); abort();
} }
out:
kvm_slots_unlock(kml);
} }
static void kvm_region_add(MemoryListener *listener, static void kvm_region_add(MemoryListener *listener,
@ -851,12 +1040,30 @@ static void kvm_log_sync(MemoryListener *listener,
KVMMemoryListener *kml = container_of(listener, KVMMemoryListener, listener); KVMMemoryListener *kml = container_of(listener, KVMMemoryListener, listener);
int r; int r;
kvm_slots_lock(kml);
r = kvm_physical_sync_dirty_bitmap(kml, section); r = kvm_physical_sync_dirty_bitmap(kml, section);
kvm_slots_unlock(kml);
if (r < 0) { if (r < 0) {
abort(); abort();
} }
} }
static void kvm_log_clear(MemoryListener *listener,
MemoryRegionSection *section)
{
KVMMemoryListener *kml = container_of(listener, KVMMemoryListener, listener);
int r;
r = kvm_physical_log_clear(kml, section);
if (r < 0) {
error_report_once("%s: kvm log clear failed: mr=%s "
"offset=%"HWADDR_PRIx" size=%"PRIx64, __func__,
section->mr->name, section->offset_within_region,
int128_get64(section->size));
abort();
}
}
static void kvm_mem_ioeventfd_add(MemoryListener *listener, static void kvm_mem_ioeventfd_add(MemoryListener *listener,
MemoryRegionSection *section, MemoryRegionSection *section,
bool match_data, uint64_t data, bool match_data, uint64_t data,
@ -935,6 +1142,7 @@ void kvm_memory_listener_register(KVMState *s, KVMMemoryListener *kml,
{ {
int i; int i;
qemu_mutex_init(&kml->slots_lock);
kml->slots = g_malloc0(s->nr_slots * sizeof(KVMSlot)); kml->slots = g_malloc0(s->nr_slots * sizeof(KVMSlot));
kml->as_id = as_id; kml->as_id = as_id;
@ -947,6 +1155,7 @@ void kvm_memory_listener_register(KVMState *s, KVMMemoryListener *kml,
kml->listener.log_start = kvm_log_start; kml->listener.log_start = kvm_log_start;
kml->listener.log_stop = kvm_log_stop; kml->listener.log_stop = kvm_log_stop;
kml->listener.log_sync = kvm_log_sync; kml->listener.log_sync = kvm_log_sync;
kml->listener.log_clear = kvm_log_clear;
kml->listener.priority = 10; kml->listener.priority = 10;
memory_listener_register(&kml->listener, as); memory_listener_register(&kml->listener, as);
@ -1671,6 +1880,17 @@ static int kvm_init(MachineState *ms)
s->coalesced_pio = s->coalesced_mmio && s->coalesced_pio = s->coalesced_mmio &&
kvm_check_extension(s, KVM_CAP_COALESCED_PIO); kvm_check_extension(s, KVM_CAP_COALESCED_PIO);
s->manual_dirty_log_protect =
kvm_check_extension(s, KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2);
if (s->manual_dirty_log_protect) {
ret = kvm_vm_enable_cap(s, KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2, 0, 1);
if (ret) {
warn_report("Trying to enable KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2 "
"but failed. Falling back to the legacy mode. ");
s->manual_dirty_log_protect = false;
}
}
#ifdef KVM_CAP_VCPU_EVENTS #ifdef KVM_CAP_VCPU_EVENTS
s->vcpu_events = kvm_check_extension(s, KVM_CAP_VCPU_EVENTS); s->vcpu_events = kvm_check_extension(s, KVM_CAP_VCPU_EVENTS);
#endif #endif

View File

@ -15,4 +15,5 @@ kvm_irqchip_release_virq(int virq) "virq %d"
kvm_set_ioeventfd_mmio(int fd, uint64_t addr, uint32_t val, bool assign, uint32_t size, bool datamatch) "fd: %d @0x%" PRIx64 " val=0x%x assign: %d size: %d match: %d" kvm_set_ioeventfd_mmio(int fd, uint64_t addr, uint32_t val, bool assign, uint32_t size, bool datamatch) "fd: %d @0x%" PRIx64 " val=0x%x assign: %d size: %d match: %d"
kvm_set_ioeventfd_pio(int fd, uint16_t addr, uint32_t val, bool assign, uint32_t size, bool datamatch) "fd: %d @0x%x val=0x%x assign: %d size: %d match: %d" kvm_set_ioeventfd_pio(int fd, uint16_t addr, uint32_t val, bool assign, uint32_t size, bool datamatch) "fd: %d @0x%x val=0x%x assign: %d size: %d match: %d"
kvm_set_user_memory(uint32_t slot, uint32_t flags, uint64_t guest_phys_addr, uint64_t memory_size, uint64_t userspace_addr, int ret) "Slot#%d flags=0x%x gpa=0x%"PRIx64 " size=0x%"PRIx64 " ua=0x%"PRIx64 " ret=%d" kvm_set_user_memory(uint32_t slot, uint32_t flags, uint64_t guest_phys_addr, uint64_t memory_size, uint64_t userspace_addr, int ret) "Slot#%d flags=0x%x gpa=0x%"PRIx64 " size=0x%"PRIx64 " ua=0x%"PRIx64 " ret=%d"
kvm_clear_dirty_log(uint32_t slot, uint64_t start, uint32_t size) "slot#%"PRId32" start 0x%"PRIx64" size 0x%"PRIx32

15
exec.c
View File

@ -1358,6 +1358,8 @@ bool cpu_physical_memory_test_and_clear_dirty(ram_addr_t start,
DirtyMemoryBlocks *blocks; DirtyMemoryBlocks *blocks;
unsigned long end, page; unsigned long end, page;
bool dirty = false; bool dirty = false;
RAMBlock *ramblock;
uint64_t mr_offset, mr_size;
if (length == 0) { if (length == 0) {
return false; return false;
@ -1369,6 +1371,10 @@ bool cpu_physical_memory_test_and_clear_dirty(ram_addr_t start,
rcu_read_lock(); rcu_read_lock();
blocks = atomic_rcu_read(&ram_list.dirty_memory[client]); blocks = atomic_rcu_read(&ram_list.dirty_memory[client]);
ramblock = qemu_get_ram_block(start);
/* Range sanity check on the ramblock */
assert(start >= ramblock->offset &&
start + length <= ramblock->offset + ramblock->used_length);
while (page < end) { while (page < end) {
unsigned long idx = page / DIRTY_MEMORY_BLOCK_SIZE; unsigned long idx = page / DIRTY_MEMORY_BLOCK_SIZE;
@ -1380,6 +1386,10 @@ bool cpu_physical_memory_test_and_clear_dirty(ram_addr_t start,
page += num; page += num;
} }
mr_offset = (ram_addr_t)(page << TARGET_PAGE_BITS) - ramblock->offset;
mr_size = (end - page) << TARGET_PAGE_BITS;
memory_region_clear_dirty_bitmap(ramblock->mr, mr_offset, mr_size);
rcu_read_unlock(); rcu_read_unlock();
if (dirty && tcg_enabled()) { if (dirty && tcg_enabled()) {
@ -1390,9 +1400,10 @@ bool cpu_physical_memory_test_and_clear_dirty(ram_addr_t start,
} }
DirtyBitmapSnapshot *cpu_physical_memory_snapshot_and_clear_dirty DirtyBitmapSnapshot *cpu_physical_memory_snapshot_and_clear_dirty
(ram_addr_t start, ram_addr_t length, unsigned client) (MemoryRegion *mr, hwaddr offset, hwaddr length, unsigned client)
{ {
DirtyMemoryBlocks *blocks; DirtyMemoryBlocks *blocks;
ram_addr_t start = memory_region_get_ram_addr(mr) + offset;
unsigned long align = 1UL << (TARGET_PAGE_BITS + BITS_PER_LEVEL); unsigned long align = 1UL << (TARGET_PAGE_BITS + BITS_PER_LEVEL);
ram_addr_t first = QEMU_ALIGN_DOWN(start, align); ram_addr_t first = QEMU_ALIGN_DOWN(start, align);
ram_addr_t last = QEMU_ALIGN_UP(start + length, align); ram_addr_t last = QEMU_ALIGN_UP(start + length, align);
@ -1434,6 +1445,8 @@ DirtyBitmapSnapshot *cpu_physical_memory_snapshot_and_clear_dirty
tlb_reset_dirty_range_all(start, length); tlb_reset_dirty_range_all(start, length);
} }
memory_region_clear_dirty_bitmap(mr, offset, length);
return snap; return snap;
} }

View File

@ -46,6 +46,8 @@
OBJECT_GET_CLASS(IOMMUMemoryRegionClass, (obj), \ OBJECT_GET_CLASS(IOMMUMemoryRegionClass, (obj), \
TYPE_IOMMU_MEMORY_REGION) TYPE_IOMMU_MEMORY_REGION)
extern bool global_dirty_log;
typedef struct MemoryRegionOps MemoryRegionOps; typedef struct MemoryRegionOps MemoryRegionOps;
typedef struct MemoryRegionMmio MemoryRegionMmio; typedef struct MemoryRegionMmio MemoryRegionMmio;
@ -414,6 +416,7 @@ struct MemoryListener {
void (*log_stop)(MemoryListener *listener, MemoryRegionSection *section, void (*log_stop)(MemoryListener *listener, MemoryRegionSection *section,
int old, int new); int old, int new);
void (*log_sync)(MemoryListener *listener, MemoryRegionSection *section); void (*log_sync)(MemoryListener *listener, MemoryRegionSection *section);
void (*log_clear)(MemoryListener *listener, MemoryRegionSection *section);
void (*log_global_start)(MemoryListener *listener); void (*log_global_start)(MemoryListener *listener);
void (*log_global_stop)(MemoryListener *listener); void (*log_global_stop)(MemoryListener *listener);
void (*eventfd_add)(MemoryListener *listener, MemoryRegionSection *section, void (*eventfd_add)(MemoryListener *listener, MemoryRegionSection *section,
@ -1267,6 +1270,22 @@ void memory_region_set_log(MemoryRegion *mr, bool log, unsigned client);
void memory_region_set_dirty(MemoryRegion *mr, hwaddr addr, void memory_region_set_dirty(MemoryRegion *mr, hwaddr addr,
hwaddr size); hwaddr size);
/**
* memory_region_clear_dirty_bitmap - clear dirty bitmap for memory range
*
* This function is called when the caller wants to clear the remote
* dirty bitmap of a memory range within the memory region. This can
* be used by e.g. KVM to manually clear dirty log when
* KVM_CAP_MANUAL_DIRTY_LOG_PROTECT is declared support by the host
* kernel.
*
* @mr: the memory region to clear the dirty log upon
* @start: start address offset within the memory region
* @len: length of the memory region to clear dirty bitmap
*/
void memory_region_clear_dirty_bitmap(MemoryRegion *mr, hwaddr start,
hwaddr len);
/** /**
* memory_region_snapshot_and_clear_dirty: Get a snapshot of the dirty * memory_region_snapshot_and_clear_dirty: Get a snapshot of the dirty
* bitmap and clear it. * bitmap and clear it.

View File

@ -51,8 +51,70 @@ struct RAMBlock {
unsigned long *unsentmap; unsigned long *unsentmap;
/* bitmap of already received pages in postcopy */ /* bitmap of already received pages in postcopy */
unsigned long *receivedmap; unsigned long *receivedmap;
/*
* bitmap to track already cleared dirty bitmap. When the bit is
* set, it means the corresponding memory chunk needs a log-clear.
* Set this up to non-NULL to enable the capability to postpone
* and split clearing of dirty bitmap on the remote node (e.g.,
* KVM). The bitmap will be set only when doing global sync.
*
* NOTE: this bitmap is different comparing to the other bitmaps
* in that one bit can represent multiple guest pages (which is
* decided by the `clear_bmap_shift' variable below). On
* destination side, this should always be NULL, and the variable
* `clear_bmap_shift' is meaningless.
*/
unsigned long *clear_bmap;
uint8_t clear_bmap_shift;
}; };
/**
* clear_bmap_size: calculate clear bitmap size
*
* @pages: number of guest pages
* @shift: guest page number shift
*
* Returns: number of bits for the clear bitmap
*/
static inline long clear_bmap_size(uint64_t pages, uint8_t shift)
{
return DIV_ROUND_UP(pages, 1UL << shift);
}
/**
* clear_bmap_set: set clear bitmap for the page range
*
* @rb: the ramblock to operate on
* @start: the start page number
* @size: number of pages to set in the bitmap
*
* Returns: None
*/
static inline void clear_bmap_set(RAMBlock *rb, uint64_t start,
uint64_t npages)
{
uint8_t shift = rb->clear_bmap_shift;
bitmap_set_atomic(rb->clear_bmap, start >> shift,
clear_bmap_size(npages, shift));
}
/**
* clear_bmap_test_and_clear: test clear bitmap for the page, clear if set
*
* @rb: the ramblock to operate on
* @page: the page number to check
*
* Returns: true if the bit was set, false otherwise
*/
static inline bool clear_bmap_test_and_clear(RAMBlock *rb, uint64_t page)
{
uint8_t shift = rb->clear_bmap_shift;
return bitmap_test_and_clear_atomic(rb->clear_bmap, page >> shift, 1);
}
static inline bool offset_in_ramblock(RAMBlock *b, ram_addr_t offset) static inline bool offset_in_ramblock(RAMBlock *b, ram_addr_t offset)
{ {
return (b && b->host && offset < b->used_length) ? true : false; return (b && b->host && offset < b->used_length) ? true : false;
@ -349,8 +411,13 @@ static inline void cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap,
if (bitmap[k]) { if (bitmap[k]) {
unsigned long temp = leul_to_cpu(bitmap[k]); unsigned long temp = leul_to_cpu(bitmap[k]);
atomic_or(&blocks[DIRTY_MEMORY_MIGRATION][idx][offset], temp);
atomic_or(&blocks[DIRTY_MEMORY_VGA][idx][offset], temp); atomic_or(&blocks[DIRTY_MEMORY_VGA][idx][offset], temp);
if (global_dirty_log) {
atomic_or(&blocks[DIRTY_MEMORY_MIGRATION][idx][offset],
temp);
}
if (tcg_enabled()) { if (tcg_enabled()) {
atomic_or(&blocks[DIRTY_MEMORY_CODE][idx][offset], temp); atomic_or(&blocks[DIRTY_MEMORY_CODE][idx][offset], temp);
} }
@ -367,6 +434,11 @@ static inline void cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap,
xen_hvm_modified_memory(start, pages << TARGET_PAGE_BITS); xen_hvm_modified_memory(start, pages << TARGET_PAGE_BITS);
} else { } else {
uint8_t clients = tcg_enabled() ? DIRTY_CLIENTS_ALL : DIRTY_CLIENTS_NOCODE; uint8_t clients = tcg_enabled() ? DIRTY_CLIENTS_ALL : DIRTY_CLIENTS_NOCODE;
if (!global_dirty_log) {
clients &= ~(1 << DIRTY_MEMORY_MIGRATION);
}
/* /*
* bitmap-traveling is faster than memory-traveling (for addr...) * bitmap-traveling is faster than memory-traveling (for addr...)
* especially when most of the memory is not dirty. * especially when most of the memory is not dirty.
@ -394,7 +466,7 @@ bool cpu_physical_memory_test_and_clear_dirty(ram_addr_t start,
unsigned client); unsigned client);
DirtyBitmapSnapshot *cpu_physical_memory_snapshot_and_clear_dirty DirtyBitmapSnapshot *cpu_physical_memory_snapshot_and_clear_dirty
(ram_addr_t start, ram_addr_t length, unsigned client); (MemoryRegion *mr, hwaddr offset, hwaddr length, unsigned client);
bool cpu_physical_memory_snapshot_get_dirty(DirtyBitmapSnapshot *snap, bool cpu_physical_memory_snapshot_get_dirty(DirtyBitmapSnapshot *snap,
ram_addr_t start, ram_addr_t start,
@ -409,6 +481,7 @@ static inline void cpu_physical_memory_clear_dirty_range(ram_addr_t start,
} }
/* Called with RCU critical section */
static inline static inline
uint64_t cpu_physical_memory_sync_dirty_bitmap(RAMBlock *rb, uint64_t cpu_physical_memory_sync_dirty_bitmap(RAMBlock *rb,
ram_addr_t start, ram_addr_t start,
@ -432,8 +505,6 @@ uint64_t cpu_physical_memory_sync_dirty_bitmap(RAMBlock *rb,
DIRTY_MEMORY_BLOCK_SIZE); DIRTY_MEMORY_BLOCK_SIZE);
unsigned long page = BIT_WORD(start >> TARGET_PAGE_BITS); unsigned long page = BIT_WORD(start >> TARGET_PAGE_BITS);
rcu_read_lock();
src = atomic_rcu_read( src = atomic_rcu_read(
&ram_list.dirty_memory[DIRTY_MEMORY_MIGRATION])->blocks; &ram_list.dirty_memory[DIRTY_MEMORY_MIGRATION])->blocks;
@ -454,7 +525,18 @@ uint64_t cpu_physical_memory_sync_dirty_bitmap(RAMBlock *rb,
} }
} }
rcu_read_unlock(); if (rb->clear_bmap) {
/*
* Postpone the dirty bitmap clear to the point before we
* really send the pages, also we will split the clear
* dirty procedure into smaller chunks.
*/
clear_bmap_set(rb, start >> TARGET_PAGE_BITS,
length >> TARGET_PAGE_BITS);
} else {
/* Slow path - still do that in a huge chunk */
memory_region_clear_dirty_bitmap(rb->mr, start, length);
}
} else { } else {
ram_addr_t offset = rb->offset; ram_addr_t offset = rb->offset;

View File

@ -41,6 +41,10 @@
* bitmap_find_next_zero_area(buf, len, pos, n, mask) Find bit free area * bitmap_find_next_zero_area(buf, len, pos, n, mask) Find bit free area
* bitmap_to_le(dst, src, nbits) Convert bitmap to little endian * bitmap_to_le(dst, src, nbits) Convert bitmap to little endian
* bitmap_from_le(dst, src, nbits) Convert bitmap from little endian * bitmap_from_le(dst, src, nbits) Convert bitmap from little endian
* bitmap_copy_with_src_offset(dst, src, offset, nbits)
* *dst = *src (with an offset into src)
* bitmap_copy_with_dst_offset(dst, src, offset, nbits)
* *dst = *src (with an offset into dst)
*/ */
/* /*
@ -271,4 +275,9 @@ void bitmap_to_le(unsigned long *dst, const unsigned long *src,
void bitmap_from_le(unsigned long *dst, const unsigned long *src, void bitmap_from_le(unsigned long *dst, const unsigned long *src,
long nbits); long nbits);
void bitmap_copy_with_src_offset(unsigned long *dst, const unsigned long *src,
unsigned long offset, unsigned long nbits);
void bitmap_copy_with_dst_offset(unsigned long *dst, const unsigned long *src,
unsigned long shift, unsigned long nbits);
#endif /* BITMAP_H */ #endif /* BITMAP_H */

View File

@ -21,10 +21,14 @@ typedef struct KVMSlot
int slot; int slot;
int flags; int flags;
int old_flags; int old_flags;
/* Dirty bitmap cache for the slot */
unsigned long *dirty_bmap;
} KVMSlot; } KVMSlot;
typedef struct KVMMemoryListener { typedef struct KVMMemoryListener {
MemoryListener listener; MemoryListener listener;
/* Protects the slots and all inside them */
QemuMutex slots_lock;
KVMSlot *slots; KVMSlot *slots;
int as_id; int as_id;
} KVMMemoryListener; } KVMMemoryListener;

View File

@ -38,7 +38,7 @@
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;
static bool global_dirty_log = false; bool global_dirty_log;
static QTAILQ_HEAD(, MemoryListener) memory_listeners static QTAILQ_HEAD(, MemoryListener) memory_listeners
= QTAILQ_HEAD_INITIALIZER(memory_listeners); = QTAILQ_HEAD_INITIALIZER(memory_listeners);
@ -2064,6 +2064,57 @@ static void memory_region_sync_dirty_bitmap(MemoryRegion *mr)
} }
} }
void memory_region_clear_dirty_bitmap(MemoryRegion *mr, hwaddr start,
hwaddr len)
{
MemoryRegionSection mrs;
MemoryListener *listener;
AddressSpace *as;
FlatView *view;
FlatRange *fr;
hwaddr sec_start, sec_end, sec_size;
QTAILQ_FOREACH(listener, &memory_listeners, link) {
if (!listener->log_clear) {
continue;
}
as = listener->address_space;
view = address_space_get_flatview(as);
FOR_EACH_FLAT_RANGE(fr, view) {
if (!fr->dirty_log_mask || fr->mr != mr) {
/*
* Clear dirty bitmap operation only applies to those
* regions whose dirty logging is at least enabled
*/
continue;
}
mrs = section_from_flat_range(fr, view);
sec_start = MAX(mrs.offset_within_region, start);
sec_end = mrs.offset_within_region + int128_get64(mrs.size);
sec_end = MIN(sec_end, start + len);
if (sec_start >= sec_end) {
/*
* If this memory region section has no intersection
* with the requested range, skip.
*/
continue;
}
/* Valid case; shrink the section if needed */
mrs.offset_within_address_space +=
sec_start - mrs.offset_within_region;
mrs.offset_within_region = sec_start;
sec_size = sec_end - sec_start;
mrs.size = int128_make64(sec_size);
listener->log_clear(listener, &mrs);
}
flatview_unref(view);
}
}
DirtyBitmapSnapshot *memory_region_snapshot_and_clear_dirty(MemoryRegion *mr, DirtyBitmapSnapshot *memory_region_snapshot_and_clear_dirty(MemoryRegion *mr,
hwaddr addr, hwaddr addr,
hwaddr size, hwaddr size,
@ -2071,8 +2122,7 @@ DirtyBitmapSnapshot *memory_region_snapshot_and_clear_dirty(MemoryRegion *mr,
{ {
assert(mr->ram_block); assert(mr->ram_block);
memory_region_sync_dirty_bitmap(mr); memory_region_sync_dirty_bitmap(mr);
return cpu_physical_memory_snapshot_and_clear_dirty( return cpu_physical_memory_snapshot_and_clear_dirty(mr, addr, size, client);
memory_region_get_ram_addr(mr) + addr, size, client);
} }
bool memory_region_snapshot_get_dirty(MemoryRegion *mr, DirtyBitmapSnapshot *snap, bool memory_region_snapshot_get_dirty(MemoryRegion *mr, DirtyBitmapSnapshot *snap,

View File

@ -3362,6 +3362,8 @@ void migration_global_dump(Monitor *mon)
ms->send_section_footer ? "on" : "off"); ms->send_section_footer ? "on" : "off");
monitor_printf(mon, "decompress-error-check: %s\n", monitor_printf(mon, "decompress-error-check: %s\n",
ms->decompress_error_check ? "on" : "off"); ms->decompress_error_check ? "on" : "off");
monitor_printf(mon, "clear-bitmap-shift: %u\n",
ms->clear_bitmap_shift);
} }
#define DEFINE_PROP_MIG_CAP(name, x) \ #define DEFINE_PROP_MIG_CAP(name, x) \
@ -3376,6 +3378,8 @@ static Property migration_properties[] = {
send_section_footer, true), send_section_footer, true),
DEFINE_PROP_BOOL("decompress-error-check", MigrationState, DEFINE_PROP_BOOL("decompress-error-check", MigrationState,
decompress_error_check, true), decompress_error_check, true),
DEFINE_PROP_UINT8("x-clear-bitmap-shift", MigrationState,
clear_bitmap_shift, CLEAR_BITMAP_SHIFT_DEFAULT),
/* Migration parameters */ /* Migration parameters */
DEFINE_PROP_UINT8("x-compress-level", MigrationState, DEFINE_PROP_UINT8("x-compress-level", MigrationState,

View File

@ -26,6 +26,23 @@ struct PostcopyBlocktimeContext;
#define MIGRATION_RESUME_ACK_VALUE (1) #define MIGRATION_RESUME_ACK_VALUE (1)
/*
* 1<<6=64 pages -> 256K chunk when page size is 4K. This gives us
* the benefit that all the chunks are 64 pages aligned then the
* bitmaps are always aligned to LONG.
*/
#define CLEAR_BITMAP_SHIFT_MIN 6
/*
* 1<<18=256K pages -> 1G chunk when page size is 4K. This is the
* default value to use if no one specified.
*/
#define CLEAR_BITMAP_SHIFT_DEFAULT 18
/*
* 1<<31=2G pages -> 8T chunk when page size is 4K. This should be
* big enough and make sure we won't overflow easily.
*/
#define CLEAR_BITMAP_SHIFT_MAX 31
/* State for the incoming migration */ /* State for the incoming migration */
struct MigrationIncomingState { struct MigrationIncomingState {
QEMUFile *from_src_file; QEMUFile *from_src_file;
@ -232,6 +249,16 @@ struct MigrationState
* do not trigger spurious decompression errors. * do not trigger spurious decompression errors.
*/ */
bool decompress_error_check; bool decompress_error_check;
/*
* This decides the size of guest memory chunk that will be used
* to track dirty bitmap clearing. The size of memory chunk will
* be GUEST_PAGE_SIZE << N. Say, N=0 means we will clear dirty
* bitmap for each page to send (1<<0=1); N=10 means we will clear
* dirty bitmap only once for 1<<10=1K continuous guest pages
* (which is in 4M chunk).
*/
uint8_t clear_bitmap_shift;
}; };
void migrate_set_state(int *state, int old_state, int new_state); void migrate_set_state(int *state, int old_state, int new_state);

View File

@ -1291,15 +1291,15 @@ static void multifd_recv_sync_main(void)
trace_multifd_recv_sync_main_wait(p->id); trace_multifd_recv_sync_main_wait(p->id);
qemu_sem_wait(&multifd_recv_state->sem_sync); qemu_sem_wait(&multifd_recv_state->sem_sync);
}
for (i = 0; i < migrate_multifd_channels(); i++) {
MultiFDRecvParams *p = &multifd_recv_state->params[i];
qemu_mutex_lock(&p->mutex); qemu_mutex_lock(&p->mutex);
if (multifd_recv_state->packet_num < p->packet_num) { if (multifd_recv_state->packet_num < p->packet_num) {
multifd_recv_state->packet_num = p->packet_num; multifd_recv_state->packet_num = p->packet_num;
} }
qemu_mutex_unlock(&p->mutex); qemu_mutex_unlock(&p->mutex);
}
for (i = 0; i < migrate_multifd_channels(); i++) {
MultiFDRecvParams *p = &multifd_recv_state->params[i];
trace_multifd_recv_sync_main_signal(p->id); trace_multifd_recv_sync_main_signal(p->id);
qemu_sem_post(&p->sem_sync); qemu_sem_post(&p->sem_sync);
} }
@ -1585,25 +1585,30 @@ static int save_xbzrle_page(RAMState *rs, uint8_t **current_data,
encoded_len = xbzrle_encode_buffer(prev_cached_page, XBZRLE.current_buf, encoded_len = xbzrle_encode_buffer(prev_cached_page, XBZRLE.current_buf,
TARGET_PAGE_SIZE, XBZRLE.encoded_buf, TARGET_PAGE_SIZE, XBZRLE.encoded_buf,
TARGET_PAGE_SIZE); TARGET_PAGE_SIZE);
/*
* Update the cache contents, so that it corresponds to the data
* sent, in all cases except where we skip the page.
*/
if (!last_stage && encoded_len != 0) {
memcpy(prev_cached_page, XBZRLE.current_buf, TARGET_PAGE_SIZE);
/*
* In the case where we couldn't compress, ensure that the caller
* sends the data from the cache, since the guest might have
* changed the RAM since we copied it.
*/
*current_data = prev_cached_page;
}
if (encoded_len == 0) { if (encoded_len == 0) {
trace_save_xbzrle_page_skipping(); trace_save_xbzrle_page_skipping();
return 0; return 0;
} else if (encoded_len == -1) { } else if (encoded_len == -1) {
trace_save_xbzrle_page_overflow(); trace_save_xbzrle_page_overflow();
xbzrle_counters.overflow++; xbzrle_counters.overflow++;
/* update data in the cache */
if (!last_stage) {
memcpy(prev_cached_page, *current_data, TARGET_PAGE_SIZE);
*current_data = prev_cached_page;
}
return -1; return -1;
} }
/* we need to update the data in the cache, in order to get the same data */
if (!last_stage) {
memcpy(prev_cached_page, XBZRLE.current_buf, TARGET_PAGE_SIZE);
}
/* Send XBZRLE based compressed page */ /* Send XBZRLE based compressed page */
bytes_xbzrle = save_page_header(rs, rs->f, block, bytes_xbzrle = save_page_header(rs, rs->f, block,
offset | RAM_SAVE_FLAG_XBZRLE); offset | RAM_SAVE_FLAG_XBZRLE);
@ -1659,6 +1664,33 @@ static inline bool migration_bitmap_clear_dirty(RAMState *rs,
bool ret; bool ret;
qemu_mutex_lock(&rs->bitmap_mutex); qemu_mutex_lock(&rs->bitmap_mutex);
/*
* Clear dirty bitmap if needed. This _must_ be called before we
* send any of the page in the chunk because we need to make sure
* we can capture further page content changes when we sync dirty
* log the next time. So as long as we are going to send any of
* the page in the chunk we clear the remote dirty bitmap for all.
* Clearing it earlier won't be a problem, but too late will.
*/
if (rb->clear_bmap && clear_bmap_test_and_clear(rb, page)) {
uint8_t shift = rb->clear_bmap_shift;
hwaddr size = 1ULL << (TARGET_PAGE_BITS + shift);
hwaddr start = (page << TARGET_PAGE_BITS) & (-size);
/*
* CLEAR_BITMAP_SHIFT_MIN should always guarantee this... this
* can make things easier sometimes since then start address
* of the small chunk will always be 64 pages aligned so the
* bitmap will always be aligned to unsigned long. We should
* even be able to remove this restriction but I'm simply
* keeping it.
*/
assert(shift >= 6);
trace_migration_bitmap_clear_dirty(rb->idstr, start, size, page);
memory_region_clear_dirty_bitmap(rb->mr, start, size);
}
ret = test_and_clear_bit(page, rb->bmap); ret = test_and_clear_bit(page, rb->bmap);
if (ret) { if (ret) {
@ -1669,6 +1701,7 @@ static inline bool migration_bitmap_clear_dirty(RAMState *rs,
return ret; return ret;
} }
/* Called with RCU critical section */
static void migration_bitmap_sync_range(RAMState *rs, RAMBlock *rb, static void migration_bitmap_sync_range(RAMState *rs, RAMBlock *rb,
ram_addr_t length) ram_addr_t length)
{ {
@ -2281,6 +2314,12 @@ static bool get_queued_page(RAMState *rs, PageSearchStatus *pss)
*/ */
pss->block = block; pss->block = block;
pss->page = offset >> TARGET_PAGE_BITS; pss->page = offset >> TARGET_PAGE_BITS;
/*
* This unqueued page would break the "one round" check, even is
* really rare.
*/
pss->complete_round = false;
} }
return !!block; return !!block;
@ -2675,6 +2714,8 @@ static void ram_save_cleanup(void *opaque)
memory_global_dirty_log_stop(); memory_global_dirty_log_stop();
RAMBLOCK_FOREACH_NOT_IGNORED(block) { RAMBLOCK_FOREACH_NOT_IGNORED(block) {
g_free(block->clear_bmap);
block->clear_bmap = NULL;
g_free(block->bmap); g_free(block->bmap);
block->bmap = NULL; block->bmap = NULL;
g_free(block->unsentmap); g_free(block->unsentmap);
@ -2763,8 +2804,7 @@ void ram_postcopy_migrated_memory_release(MigrationState *ms)
* *
* @ms: current migration state * @ms: current migration state
* @pds: state for postcopy * @pds: state for postcopy
* @start: RAMBlock starting page * @block: RAMBlock to discard
* @length: RAMBlock size
*/ */
static int postcopy_send_discard_bm_ram(MigrationState *ms, static int postcopy_send_discard_bm_ram(MigrationState *ms,
PostcopyDiscardState *pds, PostcopyDiscardState *pds,
@ -2963,7 +3003,7 @@ static void postcopy_chunk_hostpages_pass(MigrationState *ms, bool unsent_pass,
} }
/** /**
* postcopy_chuck_hostpages: discrad any partially sent host page * postcopy_chunk_hostpages: discard any partially sent host page
* *
* Utility for the outgoing postcopy code. * Utility for the outgoing postcopy code.
* *
@ -3173,11 +3213,11 @@ static int ram_state_init(RAMState **rsp)
QSIMPLEQ_INIT(&(*rsp)->src_page_requests); QSIMPLEQ_INIT(&(*rsp)->src_page_requests);
/* /*
* Count the total number of pages used by ram blocks not including any
* gaps due to alignment or unplugs.
* This must match with the initial values of dirty bitmap. * This must match with the initial values of dirty bitmap.
* Currently we initialize the dirty bitmap to all zeros so
* here the total dirty page count is zero.
*/ */
(*rsp)->migration_dirty_pages = 0; (*rsp)->migration_dirty_pages = ram_bytes_total() >> TARGET_PAGE_BITS;
ram_state_reset(*rsp); ram_state_reset(*rsp);
return 0; return 0;
@ -3185,23 +3225,39 @@ static int ram_state_init(RAMState **rsp)
static void ram_list_init_bitmaps(void) static void ram_list_init_bitmaps(void)
{ {
MigrationState *ms = migrate_get_current();
RAMBlock *block; RAMBlock *block;
unsigned long pages; unsigned long pages;
uint8_t shift;
/* Skip setting bitmap if there is no RAM */ /* Skip setting bitmap if there is no RAM */
if (ram_bytes_total()) { if (ram_bytes_total()) {
shift = ms->clear_bitmap_shift;
if (shift > CLEAR_BITMAP_SHIFT_MAX) {
error_report("clear_bitmap_shift (%u) too big, using "
"max value (%u)", shift, CLEAR_BITMAP_SHIFT_MAX);
shift = CLEAR_BITMAP_SHIFT_MAX;
} else if (shift < CLEAR_BITMAP_SHIFT_MIN) {
error_report("clear_bitmap_shift (%u) too small, using "
"min value (%u)", shift, CLEAR_BITMAP_SHIFT_MIN);
shift = CLEAR_BITMAP_SHIFT_MIN;
}
RAMBLOCK_FOREACH_NOT_IGNORED(block) { RAMBLOCK_FOREACH_NOT_IGNORED(block) {
pages = block->max_length >> TARGET_PAGE_BITS; pages = block->max_length >> TARGET_PAGE_BITS;
/* /*
* The initial dirty bitmap for migration must be set with all * The initial dirty bitmap for migration must be set with all
* ones to make sure we'll migrate every guest RAM page to * ones to make sure we'll migrate every guest RAM page to
* destination. * destination.
* Here we didn't set RAMBlock.bmap simply because it is already * Here we set RAMBlock.bmap all to 1 because when rebegin a
* set in ram_list.dirty_memory[DIRTY_MEMORY_MIGRATION] in * new migration after a failed migration, ram_list.
* ram_block_add, and that's where we'll sync the dirty bitmaps. * dirty_memory[DIRTY_MEMORY_MIGRATION] don't include the whole
* Here setting RAMBlock.bmap would be fine too but not necessary. * guest memory.
*/ */
block->bmap = bitmap_new(pages); block->bmap = bitmap_new(pages);
bitmap_set(block->bmap, 0, pages);
block->clear_bmap_shift = shift;
block->clear_bmap = bitmap_new(clear_bmap_size(pages, shift));
if (migrate_postcopy_ram()) { if (migrate_postcopy_ram()) {
block->unsentmap = bitmap_new(pages); block->unsentmap = bitmap_new(pages);
bitmap_set(block->unsentmap, 0, pages); bitmap_set(block->unsentmap, 0, pages);
@ -3370,7 +3426,6 @@ static int ram_save_setup(QEMUFile *f, void *opaque)
} }
if (migrate_ignore_shared()) { if (migrate_ignore_shared()) {
qemu_put_be64(f, block->mr->addr); qemu_put_be64(f, block->mr->addr);
qemu_put_byte(f, ramblock_is_ignored(block) ? 1 : 0);
} }
} }
@ -3466,8 +3521,8 @@ static int ram_save_iterate(QEMUFile *f, void *opaque)
*/ */
ram_control_after_iterate(f, RAM_CONTROL_ROUND); ram_control_after_iterate(f, RAM_CONTROL_ROUND);
multifd_send_sync_main();
out: out:
multifd_send_sync_main();
qemu_put_be64(f, RAM_SAVE_FLAG_EOS); qemu_put_be64(f, RAM_SAVE_FLAG_EOS);
qemu_fflush(f); qemu_fflush(f);
ram_counters.transferred += 8; ram_counters.transferred += 8;
@ -4337,12 +4392,6 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
} }
if (migrate_ignore_shared()) { if (migrate_ignore_shared()) {
hwaddr addr = qemu_get_be64(f); hwaddr addr = qemu_get_be64(f);
bool ignored = qemu_get_byte(f);
if (ignored != ramblock_is_ignored(block)) {
error_report("RAM block %s should %s be migrated",
id, ignored ? "" : "not");
ret = -EINVAL;
}
if (ramblock_is_ignored(block) && if (ramblock_is_ignored(block) &&
block->mr->addr != addr) { block->mr->addr != addr) {
error_report("Mismatched GPAs for block %s " error_report("Mismatched GPAs for block %s "

View File

@ -1863,7 +1863,6 @@ static void loadvm_postcopy_handle_run_bh(void *opaque)
} }
trace_loadvm_postcopy_handle_run_cpu_sync(); trace_loadvm_postcopy_handle_run_cpu_sync();
cpu_synchronize_all_post_init();
trace_loadvm_postcopy_handle_run_vmstart(); trace_loadvm_postcopy_handle_run_vmstart();

View File

@ -79,8 +79,9 @@ get_queued_page(const char *block_name, uint64_t tmp_offset, unsigned long page_
get_queued_page_not_dirty(const char *block_name, uint64_t tmp_offset, unsigned long page_abs, int sent) "%s/0x%" PRIx64 " page_abs=0x%lx (sent=%d)" get_queued_page_not_dirty(const char *block_name, uint64_t tmp_offset, unsigned long page_abs, int sent) "%s/0x%" PRIx64 " page_abs=0x%lx (sent=%d)"
migration_bitmap_sync_start(void) "" migration_bitmap_sync_start(void) ""
migration_bitmap_sync_end(uint64_t dirty_pages) "dirty_pages %" PRIu64 migration_bitmap_sync_end(uint64_t dirty_pages) "dirty_pages %" PRIu64
migration_bitmap_clear_dirty(char *str, uint64_t start, uint64_t size, unsigned long page) "rb %s start 0x%"PRIx64" size 0x%"PRIx64" page 0x%lx"
migration_throttle(void) "" migration_throttle(void) ""
multifd_recv(uint8_t id, uint64_t packet_num, uint32_t used, uint32_t flags, uint32_t next_packet_size) "channel %d packet number %" PRIu64 " pages %d flags 0x%x next packet size %d" multifd_recv(uint8_t id, uint64_t packet_num, uint32_t used, uint32_t flags, uint32_t next_packet_size) "channel %d packet_num %" PRIu64 " pages %d flags 0x%x next packet size %d"
multifd_recv_sync_main(long packet_num) "packet num %ld" multifd_recv_sync_main(long packet_num) "packet num %ld"
multifd_recv_sync_main_signal(uint8_t id) "channel %d" multifd_recv_sync_main_signal(uint8_t id) "channel %d"
multifd_recv_sync_main_wait(uint8_t id) "channel %d" multifd_recv_sync_main_wait(uint8_t id) "channel %d"

View File

@ -65,6 +65,7 @@ check-unit-y += tests/test-opts-visitor$(EXESUF)
check-unit-$(CONFIG_BLOCK) += tests/test-coroutine$(EXESUF) check-unit-$(CONFIG_BLOCK) += tests/test-coroutine$(EXESUF)
check-unit-y += tests/test-visitor-serialization$(EXESUF) check-unit-y += tests/test-visitor-serialization$(EXESUF)
check-unit-y += tests/test-iov$(EXESUF) check-unit-y += tests/test-iov$(EXESUF)
check-unit-y += tests/test-bitmap$(EXESUF)
check-unit-$(CONFIG_BLOCK) += tests/test-aio$(EXESUF) check-unit-$(CONFIG_BLOCK) += tests/test-aio$(EXESUF)
check-unit-$(CONFIG_BLOCK) += tests/test-aio-multithread$(EXESUF) check-unit-$(CONFIG_BLOCK) += tests/test-aio-multithread$(EXESUF)
check-unit-$(CONFIG_BLOCK) += tests/test-throttle$(EXESUF) check-unit-$(CONFIG_BLOCK) += tests/test-throttle$(EXESUF)
@ -538,6 +539,7 @@ tests/test-image-locking$(EXESUF): tests/test-image-locking.o $(test-block-obj-y
tests/test-thread-pool$(EXESUF): tests/test-thread-pool.o $(test-block-obj-y) tests/test-thread-pool$(EXESUF): tests/test-thread-pool.o $(test-block-obj-y)
tests/test-iov$(EXESUF): tests/test-iov.o $(test-util-obj-y) tests/test-iov$(EXESUF): tests/test-iov.o $(test-util-obj-y)
tests/test-hbitmap$(EXESUF): tests/test-hbitmap.o $(test-util-obj-y) $(test-crypto-obj-y) tests/test-hbitmap$(EXESUF): tests/test-hbitmap.o $(test-util-obj-y) $(test-crypto-obj-y)
tests/test-bitmap$(EXESUF): tests/test-bitmap.o $(test-util-obj-y)
tests/test-x86-cpuid$(EXESUF): tests/test-x86-cpuid.o tests/test-x86-cpuid$(EXESUF): tests/test-x86-cpuid.o
tests/test-xbzrle$(EXESUF): tests/test-xbzrle.o migration/xbzrle.o migration/page_cache.o $(test-util-obj-y) tests/test-xbzrle$(EXESUF): tests/test-xbzrle.o migration/xbzrle.o migration/page_cache.o $(test-util-obj-y)
tests/test-cutils$(EXESUF): tests/test-cutils.o util/cutils.o $(test-util-obj-y) tests/test-cutils$(EXESUF): tests/test-cutils.o util/cutils.o $(test-util-obj-y)

View File

@ -398,7 +398,8 @@ static char *migrate_get_socket_address(QTestState *who, const char *parameter)
return result; return result;
} }
static long long migrate_get_parameter(QTestState *who, const char *parameter) static long long migrate_get_parameter_int(QTestState *who,
const char *parameter)
{ {
QDict *rsp; QDict *rsp;
long long result; long long result;
@ -409,17 +410,17 @@ static long long migrate_get_parameter(QTestState *who, const char *parameter)
return result; return result;
} }
static void migrate_check_parameter(QTestState *who, const char *parameter, static void migrate_check_parameter_int(QTestState *who, const char *parameter,
long long value) long long value)
{ {
long long result; long long result;
result = migrate_get_parameter(who, parameter); result = migrate_get_parameter_int(who, parameter);
g_assert_cmpint(result, ==, value); g_assert_cmpint(result, ==, value);
} }
static void migrate_set_parameter(QTestState *who, const char *parameter, static void migrate_set_parameter_int(QTestState *who, const char *parameter,
long long value) long long value)
{ {
QDict *rsp; QDict *rsp;
@ -429,7 +430,7 @@ static void migrate_set_parameter(QTestState *who, const char *parameter,
parameter, value); parameter, value);
g_assert(qdict_haskey(rsp, "return")); g_assert(qdict_haskey(rsp, "return"));
qobject_unref(rsp); qobject_unref(rsp);
migrate_check_parameter(who, parameter, value); migrate_check_parameter_int(who, parameter, value);
} }
static void migrate_pause(QTestState *who) static void migrate_pause(QTestState *who)
@ -681,7 +682,7 @@ static void deprecated_set_downtime(QTestState *who, const double value)
" 'arguments': { 'value': %f } }", value); " 'arguments': { 'value': %f } }", value);
g_assert(qdict_haskey(rsp, "return")); g_assert(qdict_haskey(rsp, "return"));
qobject_unref(rsp); qobject_unref(rsp);
migrate_check_parameter(who, "downtime-limit", value * 1000); migrate_check_parameter_int(who, "downtime-limit", value * 1000);
} }
static void deprecated_set_speed(QTestState *who, long long value) static void deprecated_set_speed(QTestState *who, long long value)
@ -692,7 +693,7 @@ static void deprecated_set_speed(QTestState *who, long long value)
"'arguments': { 'value': %lld } }", value); "'arguments': { 'value': %lld } }", value);
g_assert(qdict_haskey(rsp, "return")); g_assert(qdict_haskey(rsp, "return"));
qobject_unref(rsp); qobject_unref(rsp);
migrate_check_parameter(who, "max-bandwidth", value); migrate_check_parameter_int(who, "max-bandwidth", value);
} }
static void deprecated_set_cache_size(QTestState *who, long long value) static void deprecated_set_cache_size(QTestState *who, long long value)
@ -703,7 +704,7 @@ static void deprecated_set_cache_size(QTestState *who, long long value)
"'arguments': { 'value': %lld } }", value); "'arguments': { 'value': %lld } }", value);
g_assert(qdict_haskey(rsp, "return")); g_assert(qdict_haskey(rsp, "return"));
qobject_unref(rsp); qobject_unref(rsp);
migrate_check_parameter(who, "xbzrle-cache-size", value); migrate_check_parameter_int(who, "xbzrle-cache-size", value);
} }
static void test_deprecated(void) static void test_deprecated(void)
@ -738,8 +739,8 @@ static int migrate_postcopy_prepare(QTestState **from_ptr,
* quickly, but that it doesn't complete precopy even on a slow * quickly, but that it doesn't complete precopy even on a slow
* machine, so also set the downtime. * machine, so also set the downtime.
*/ */
migrate_set_parameter(from, "max-bandwidth", 100000000); migrate_set_parameter_int(from, "max-bandwidth", 100000000);
migrate_set_parameter(from, "downtime-limit", 1); migrate_set_parameter_int(from, "downtime-limit", 1);
/* Wait for the first serial output from the source */ /* Wait for the first serial output from the source */
wait_for_serial("src_serial"); wait_for_serial("src_serial");
@ -790,7 +791,7 @@ static void test_postcopy_recovery(void)
} }
/* Turn postcopy speed down, 4K/s is slow enough on any machines */ /* Turn postcopy speed down, 4K/s is slow enough on any machines */
migrate_set_parameter(from, "max-postcopy-bandwidth", 4096); migrate_set_parameter_int(from, "max-postcopy-bandwidth", 4096);
/* Now we start the postcopy */ /* Now we start the postcopy */
migrate_postcopy_start(from, to); migrate_postcopy_start(from, to);
@ -831,7 +832,7 @@ static void test_postcopy_recovery(void)
g_free(uri); g_free(uri);
/* Restore the postcopy bandwidth to unlimited */ /* Restore the postcopy bandwidth to unlimited */
migrate_set_parameter(from, "max-postcopy-bandwidth", 0); migrate_set_parameter_int(from, "max-postcopy-bandwidth", 0);
migrate_postcopy_complete(from, to); migrate_postcopy_complete(from, to);
} }
@ -877,9 +878,9 @@ static void test_precopy_unix(void)
* machine, so also set the downtime. * machine, so also set the downtime.
*/ */
/* 1 ms should make it not converge*/ /* 1 ms should make it not converge*/
migrate_set_parameter(from, "downtime-limit", 1); migrate_set_parameter_int(from, "downtime-limit", 1);
/* 1GB/s */ /* 1GB/s */
migrate_set_parameter(from, "max-bandwidth", 1000000000); migrate_set_parameter_int(from, "max-bandwidth", 1000000000);
/* Wait for the first serial output from the source */ /* Wait for the first serial output from the source */
wait_for_serial("src_serial"); wait_for_serial("src_serial");
@ -889,7 +890,7 @@ static void test_precopy_unix(void)
wait_for_migration_pass(from); wait_for_migration_pass(from);
/* 300 ms should converge */ /* 300 ms should converge */
migrate_set_parameter(from, "downtime-limit", 300); migrate_set_parameter_int(from, "downtime-limit", 300);
if (!got_stop) { if (!got_stop) {
qtest_qmp_eventwait(from, "STOP"); qtest_qmp_eventwait(from, "STOP");
@ -956,11 +957,11 @@ static void test_xbzrle(const char *uri)
* machine, so also set the downtime. * machine, so also set the downtime.
*/ */
/* 1 ms should make it not converge*/ /* 1 ms should make it not converge*/
migrate_set_parameter(from, "downtime-limit", 1); migrate_set_parameter_int(from, "downtime-limit", 1);
/* 1GB/s */ /* 1GB/s */
migrate_set_parameter(from, "max-bandwidth", 1000000000); migrate_set_parameter_int(from, "max-bandwidth", 1000000000);
migrate_set_parameter(from, "xbzrle-cache-size", 33554432); migrate_set_parameter_int(from, "xbzrle-cache-size", 33554432);
migrate_set_capability(from, "xbzrle", "true"); migrate_set_capability(from, "xbzrle", "true");
migrate_set_capability(to, "xbzrle", "true"); migrate_set_capability(to, "xbzrle", "true");
@ -972,7 +973,7 @@ static void test_xbzrle(const char *uri)
wait_for_migration_pass(from); wait_for_migration_pass(from);
/* 300ms should converge */ /* 300ms should converge */
migrate_set_parameter(from, "downtime-limit", 300); migrate_set_parameter_int(from, "downtime-limit", 300);
if (!got_stop) { if (!got_stop) {
qtest_qmp_eventwait(from, "STOP"); qtest_qmp_eventwait(from, "STOP");
@ -1008,9 +1009,9 @@ static void test_precopy_tcp(void)
* machine, so also set the downtime. * machine, so also set the downtime.
*/ */
/* 1 ms should make it not converge*/ /* 1 ms should make it not converge*/
migrate_set_parameter(from, "downtime-limit", 1); migrate_set_parameter_int(from, "downtime-limit", 1);
/* 1GB/s */ /* 1GB/s */
migrate_set_parameter(from, "max-bandwidth", 1000000000); migrate_set_parameter_int(from, "max-bandwidth", 1000000000);
/* Wait for the first serial output from the source */ /* Wait for the first serial output from the source */
wait_for_serial("src_serial"); wait_for_serial("src_serial");
@ -1022,7 +1023,7 @@ static void test_precopy_tcp(void)
wait_for_migration_pass(from); wait_for_migration_pass(from);
/* 300ms should converge */ /* 300ms should converge */
migrate_set_parameter(from, "downtime-limit", 300); migrate_set_parameter_int(from, "downtime-limit", 300);
if (!got_stop) { if (!got_stop) {
qtest_qmp_eventwait(from, "STOP"); qtest_qmp_eventwait(from, "STOP");
@ -1054,9 +1055,9 @@ static void test_migrate_fd_proto(void)
* machine, so also set the downtime. * machine, so also set the downtime.
*/ */
/* 1 ms should make it not converge */ /* 1 ms should make it not converge */
migrate_set_parameter(from, "downtime-limit", 1); migrate_set_parameter_int(from, "downtime-limit", 1);
/* 1GB/s */ /* 1GB/s */
migrate_set_parameter(from, "max-bandwidth", 1000000000); migrate_set_parameter_int(from, "max-bandwidth", 1000000000);
/* Wait for the first serial output from the source */ /* Wait for the first serial output from the source */
wait_for_serial("src_serial"); wait_for_serial("src_serial");
@ -1090,7 +1091,7 @@ static void test_migrate_fd_proto(void)
wait_for_migration_pass(from); wait_for_migration_pass(from);
/* 300ms should converge */ /* 300ms should converge */
migrate_set_parameter(from, "downtime-limit", 300); migrate_set_parameter_int(from, "downtime-limit", 300);
if (!got_stop) { if (!got_stop) {
qtest_qmp_eventwait(from, "STOP"); qtest_qmp_eventwait(from, "STOP");

72
tests/test-bitmap.c Normal file
View File

@ -0,0 +1,72 @@
/*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* Bitmap.c unit-tests.
*
* Copyright (C) 2019, Red Hat, Inc.
*
* Author: Peter Xu <peterx@redhat.com>
*/
#include <stdlib.h>
#include "qemu/osdep.h"
#include "qemu/bitmap.h"
#define BMAP_SIZE 1024
static void check_bitmap_copy_with_offset(void)
{
unsigned long *bmap1, *bmap2, *bmap3, total;
bmap1 = bitmap_new(BMAP_SIZE);
bmap2 = bitmap_new(BMAP_SIZE);
bmap3 = bitmap_new(BMAP_SIZE);
bmap1[0] = random();
bmap1[1] = random();
bmap1[2] = random();
bmap1[3] = random();
total = BITS_PER_LONG * 4;
/* Shift 115 bits into bmap2 */
bitmap_copy_with_dst_offset(bmap2, bmap1, 115, total);
/* Shift another 85 bits into bmap3 */
bitmap_copy_with_dst_offset(bmap3, bmap2, 85, total + 115);
/* Shift back 200 bits back */
bitmap_copy_with_src_offset(bmap2, bmap3, 200, total);
g_assert_cmpmem(bmap1, total / BITS_PER_LONG,
bmap2, total / BITS_PER_LONG);
bitmap_clear(bmap1, 0, BMAP_SIZE);
/* Set bits in bmap1 are 100-245 */
bitmap_set(bmap1, 100, 145);
/* Set bits in bmap2 are 60-205 */
bitmap_copy_with_src_offset(bmap2, bmap1, 40, 250);
g_assert_cmpint(find_first_bit(bmap2, 60), ==, 60);
g_assert_cmpint(find_next_zero_bit(bmap2, 205, 60), ==, 205);
g_assert(test_bit(205, bmap2) == 0);
/* Set bits in bmap3 are 135-280 */
bitmap_copy_with_dst_offset(bmap3, bmap1, 35, 250);
g_assert_cmpint(find_first_bit(bmap3, 135), ==, 135);
g_assert_cmpint(find_next_zero_bit(bmap3, 280, 135), ==, 280);
g_assert(test_bit(280, bmap3) == 0);
g_free(bmap1);
g_free(bmap2);
g_free(bmap3);
}
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func("/bitmap/bitmap_copy_with_offset",
check_bitmap_copy_with_offset);
g_test_run();
return 0;
}

View File

@ -402,3 +402,88 @@ void bitmap_to_le(unsigned long *dst, const unsigned long *src,
{ {
bitmap_to_from_le(dst, src, nbits); bitmap_to_from_le(dst, src, nbits);
} }
/*
* Copy "src" bitmap with a positive offset and put it into the "dst"
* bitmap. The caller needs to make sure the bitmap size of "src"
* is bigger than (shift + nbits).
*/
void bitmap_copy_with_src_offset(unsigned long *dst, const unsigned long *src,
unsigned long shift, unsigned long nbits)
{
unsigned long left_mask, right_mask, last_mask;
/* Proper shift src pointer to the first word to copy from */
src += BIT_WORD(shift);
shift %= BITS_PER_LONG;
if (!shift) {
/* Fast path */
bitmap_copy(dst, src, nbits);
return;
}
right_mask = (1ul << shift) - 1;
left_mask = ~right_mask;
while (nbits >= BITS_PER_LONG) {
*dst = (*src & left_mask) >> shift;
*dst |= (src[1] & right_mask) << (BITS_PER_LONG - shift);
dst++;
src++;
nbits -= BITS_PER_LONG;
}
if (nbits > BITS_PER_LONG - shift) {
*dst = (*src & left_mask) >> shift;
nbits -= BITS_PER_LONG - shift;
last_mask = (1ul << nbits) - 1;
*dst |= (src[1] & last_mask) << (BITS_PER_LONG - shift);
} else if (nbits) {
last_mask = (1ul << nbits) - 1;
*dst = (*src >> shift) & last_mask;
}
}
/*
* Copy "src" bitmap into the "dst" bitmap with an offset in the
* "dst". The caller needs to make sure the bitmap size of "dst" is
* bigger than (shift + nbits).
*/
void bitmap_copy_with_dst_offset(unsigned long *dst, const unsigned long *src,
unsigned long shift, unsigned long nbits)
{
unsigned long left_mask, right_mask, last_mask;
/* Proper shift dst pointer to the first word to copy from */
dst += BIT_WORD(shift);
shift %= BITS_PER_LONG;
if (!shift) {
/* Fast path */
bitmap_copy(dst, src, nbits);
return;
}
right_mask = (1ul << (BITS_PER_LONG - shift)) - 1;
left_mask = ~right_mask;
*dst &= (1ul << shift) - 1;
while (nbits >= BITS_PER_LONG) {
*dst |= (*src & right_mask) << shift;
dst[1] = (*src & left_mask) >> (BITS_PER_LONG - shift);
dst++;
src++;
nbits -= BITS_PER_LONG;
}
if (nbits > BITS_PER_LONG - shift) {
*dst |= (*src & right_mask) << shift;
nbits -= BITS_PER_LONG - shift;
last_mask = ((1ul << nbits) - 1) << (BITS_PER_LONG - shift);
dst[1] = (*src & last_mask) >> (BITS_PER_LONG - shift);
} else if (nbits) {
last_mask = (1ul << nbits) - 1;
*dst |= (*src & last_mask) << shift;
}
}

View File

@ -756,11 +756,11 @@ int uleb128_encode_small(uint8_t *out, uint32_t n)
{ {
g_assert(n <= 0x3fff); g_assert(n <= 0x3fff);
if (n < 0x80) { if (n < 0x80) {
*out++ = n; *out = n;
return 1; return 1;
} else { } else {
*out++ = (n & 0x7f) | 0x80; *out++ = (n & 0x7f) | 0x80;
*out++ = n >> 7; *out = n >> 7;
return 2; return 2;
} }
} }
@ -768,7 +768,7 @@ int uleb128_encode_small(uint8_t *out, uint32_t n)
int uleb128_decode_small(const uint8_t *in, uint32_t *n) int uleb128_decode_small(const uint8_t *in, uint32_t *n)
{ {
if (!(*in & 0x80)) { if (!(*in & 0x80)) {
*n = *in++; *n = *in;
return 1; return 1;
} else { } else {
*n = *in++ & 0x7f; *n = *in++ & 0x7f;
@ -776,7 +776,7 @@ int uleb128_decode_small(const uint8_t *in, uint32_t *n)
if (*in & 0x80) { if (*in & 0x80) {
return -1; return -1;
} }
*n |= *in++ << 7; *n |= *in << 7;
return 2; return 2;
} }
} }