exec: protect mru_block with RCU

Hence, freeing a RAMBlock has to be switched to call_rcu.

Reviewed-by: Fam Zheng <famz@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
Paolo Bonzini 2013-09-09 17:58:40 +02:00
parent 439c5e02d5
commit 43771539d4
2 changed files with 39 additions and 15 deletions

52
exec.c
View File

@ -811,7 +811,7 @@ static RAMBlock *qemu_get_ram_block(ram_addr_t addr)
RAMBlock *block;
/* The list is protected by the iothread lock here. */
block = ram_list.mru_block;
block = atomic_rcu_read(&ram_list.mru_block);
if (block && addr - block->offset < block->max_length) {
goto found;
}
@ -825,6 +825,22 @@ static RAMBlock *qemu_get_ram_block(ram_addr_t addr)
abort();
found:
/* It is safe to write mru_block outside the iothread lock. This
* is what happens:
*
* mru_block = xxx
* rcu_read_unlock()
* xxx removed from list
* rcu_read_lock()
* read mru_block
* mru_block = NULL;
* call_rcu(reclaim_ramblock, xxx);
* rcu_read_unlock()
*
* atomic_rcu_set is not needed here. The block was already published
* when it was placed into the list. Here we're just making an extra
* copy of the pointer.
*/
ram_list.mru_block = block;
return block;
}
@ -1526,13 +1542,31 @@ void qemu_ram_free_from_ptr(ram_addr_t addr)
QTAILQ_REMOVE(&ram_list.blocks, block, next);
ram_list.mru_block = NULL;
ram_list.version++;
g_free(block);
g_free_rcu(block, rcu);
break;
}
}
qemu_mutex_unlock_ramlist();
}
static void reclaim_ramblock(RAMBlock *block)
{
if (block->flags & RAM_PREALLOC) {
;
} else if (xen_enabled()) {
xen_invalidate_map_cache_entry(block->host);
#ifndef _WIN32
} else if (block->fd >= 0) {
munmap(block->host, block->max_length);
close(block->fd);
#endif
} else {
qemu_anon_ram_free(block->host, block->max_length);
}
g_free(block);
}
/* Called with the iothread lock held */
void qemu_ram_free(ram_addr_t addr)
{
RAMBlock *block;
@ -1544,19 +1578,7 @@ void qemu_ram_free(ram_addr_t addr)
QTAILQ_REMOVE(&ram_list.blocks, block, next);
ram_list.mru_block = NULL;
ram_list.version++;
if (block->flags & RAM_PREALLOC) {
;
} else if (xen_enabled()) {
xen_invalidate_map_cache_entry(block->host);
#ifndef _WIN32
} else if (block->fd >= 0) {
munmap(block->host, block->max_length);
close(block->fd);
#endif
} else {
qemu_anon_ram_free(block->host, block->max_length);
}
g_free(block);
call_rcu(block, reclaim_ramblock, rcu);
break;
}
}

View File

@ -24,6 +24,7 @@
#include "exec/memory.h"
#include "qemu/thread.h"
#include "qom/cpu.h"
#include "qemu/rcu.h"
/* some important defines:
*
@ -268,6 +269,7 @@ CPUArchState *cpu_copy(CPUArchState *env);
typedef struct RAMBlock RAMBlock;
struct RAMBlock {
struct rcu_head rcu;
struct MemoryRegion *mr;
uint8_t *host;
ram_addr_t offset;