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; RAMBlock *block;
/* The list is protected by the iothread lock here. */ /* 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) { if (block && addr - block->offset < block->max_length) {
goto found; goto found;
} }
@ -825,6 +825,22 @@ static RAMBlock *qemu_get_ram_block(ram_addr_t addr)
abort(); abort();
found: 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; ram_list.mru_block = block;
return block; return block;
} }
@ -1526,13 +1542,31 @@ void qemu_ram_free_from_ptr(ram_addr_t addr)
QTAILQ_REMOVE(&ram_list.blocks, block, next); QTAILQ_REMOVE(&ram_list.blocks, block, next);
ram_list.mru_block = NULL; ram_list.mru_block = NULL;
ram_list.version++; ram_list.version++;
g_free(block); g_free_rcu(block, rcu);
break; break;
} }
} }
qemu_mutex_unlock_ramlist(); 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) void qemu_ram_free(ram_addr_t addr)
{ {
RAMBlock *block; RAMBlock *block;
@ -1544,19 +1578,7 @@ void qemu_ram_free(ram_addr_t addr)
QTAILQ_REMOVE(&ram_list.blocks, block, next); QTAILQ_REMOVE(&ram_list.blocks, block, next);
ram_list.mru_block = NULL; ram_list.mru_block = NULL;
ram_list.version++; ram_list.version++;
if (block->flags & RAM_PREALLOC) { call_rcu(block, reclaim_ramblock, rcu);
;
} 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);
break; break;
} }
} }

View File

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