Skip to content

Commit

Permalink
fix qemu exit on memory hotplug when allocation fails at prealloc time
Browse files Browse the repository at this point in the history
When adding hostmem backend at runtime, QEMU might exit with error:
  "os_mem_prealloc: Insufficient free host memory pages available to allocate guest RAM"

It happens due to os_mem_prealloc() not handling errors gracefully.

Fix it by passing errp argument so that os_mem_prealloc() could
report error to callers and undo performed allocation when
os_mem_prealloc() fails.

Signed-off-by: Igor Mammedov <imammedo@redhat.com>
Message-Id: <1469008443-72059-1-git-send-email-imammedo@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
  • Loading branch information
Igor Mammedov authored and bonzini committed Aug 2, 2016
1 parent 0b21757 commit 056b68a
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 21 deletions.
18 changes: 14 additions & 4 deletions backends/hostmem.c
Expand Up @@ -203,6 +203,7 @@ static bool host_memory_backend_get_prealloc(Object *obj, Error **errp)
static void host_memory_backend_set_prealloc(Object *obj, bool value,
Error **errp)
{
Error *local_err = NULL;
HostMemoryBackend *backend = MEMORY_BACKEND(obj);

if (backend->force_prealloc) {
Expand All @@ -223,7 +224,11 @@ static void host_memory_backend_set_prealloc(Object *obj, bool value,
void *ptr = memory_region_get_ram_ptr(&backend->mr);
uint64_t sz = memory_region_size(&backend->mr);

os_mem_prealloc(fd, ptr, sz);
os_mem_prealloc(fd, ptr, sz, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
}
backend->prealloc = true;
}
}
Expand Down Expand Up @@ -286,8 +291,7 @@ host_memory_backend_memory_complete(UserCreatable *uc, Error **errp)
if (bc->alloc) {
bc->alloc(backend, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
goto out;
}

ptr = memory_region_get_ram_ptr(&backend->mr);
Expand Down Expand Up @@ -343,9 +347,15 @@ host_memory_backend_memory_complete(UserCreatable *uc, Error **errp)
* specified NUMA policy in place.
*/
if (backend->prealloc) {
os_mem_prealloc(memory_region_get_fd(&backend->mr), ptr, sz);
os_mem_prealloc(memory_region_get_fd(&backend->mr), ptr, sz,
&local_err);
if (local_err) {
goto out;
}
}
}
out:
error_propagate(errp, local_err);
}

static bool
Expand Down
10 changes: 8 additions & 2 deletions exec.c
Expand Up @@ -1226,7 +1226,7 @@ static void *file_ram_alloc(RAMBlock *block,
char *filename;
char *sanitized_name;
char *c;
void *area;
void *area = MAP_FAILED;
int fd = -1;
int64_t page_size;

Expand Down Expand Up @@ -1314,13 +1314,19 @@ static void *file_ram_alloc(RAMBlock *block,
}

if (mem_prealloc) {
os_mem_prealloc(fd, area, memory);
os_mem_prealloc(fd, area, memory, errp);
if (errp && *errp) {
goto error;
}
}

block->fd = fd;
return area;

error:
if (area != MAP_FAILED) {
qemu_ram_munmap(area, memory);
}
if (unlink_on_error) {
unlink(path);
}
Expand Down
2 changes: 1 addition & 1 deletion include/qemu/osdep.h
Expand Up @@ -379,7 +379,7 @@ unsigned long qemu_getauxval(unsigned long type);

void qemu_set_tty_echo(int fd, bool echo);

void os_mem_prealloc(int fd, char *area, size_t sz);
void os_mem_prealloc(int fd, char *area, size_t sz, Error **errp);

int qemu_read_password(char *buf, int buf_size);

Expand Down
26 changes: 13 additions & 13 deletions util/oslib-posix.c
Expand Up @@ -318,7 +318,7 @@ static void sigbus_handler(int signal)
siglongjmp(sigjump, 1);
}

void os_mem_prealloc(int fd, char *area, size_t memory)
void os_mem_prealloc(int fd, char *area, size_t memory, Error **errp)
{
int ret;
struct sigaction act, oldact;
Expand All @@ -330,8 +330,9 @@ void os_mem_prealloc(int fd, char *area, size_t memory)

ret = sigaction(SIGBUS, &act, &oldact);
if (ret) {
perror("os_mem_prealloc: failed to install signal handler");
exit(1);
error_setg_errno(errp, errno,
"os_mem_prealloc: failed to install signal handler");
return;
}

/* unblock SIGBUS */
Expand All @@ -340,9 +341,8 @@ void os_mem_prealloc(int fd, char *area, size_t memory)
pthread_sigmask(SIG_UNBLOCK, &set, &oldset);

if (sigsetjmp(sigjump, 1)) {
fprintf(stderr, "os_mem_prealloc: Insufficient free host memory "
"pages available to allocate guest RAM\n");
exit(1);
error_setg(errp, "os_mem_prealloc: Insufficient free host memory "
"pages available to allocate guest RAM\n");
} else {
int i;
size_t hpagesize = qemu_fd_getpagesize(fd);
Expand All @@ -352,15 +352,15 @@ void os_mem_prealloc(int fd, char *area, size_t memory)
for (i = 0; i < numpages; i++) {
memset(area + (hpagesize * i), 0, 1);
}
}

ret = sigaction(SIGBUS, &oldact, NULL);
if (ret) {
perror("os_mem_prealloc: failed to reinstall signal handler");
exit(1);
}

pthread_sigmask(SIG_SETMASK, &oldset, NULL);
ret = sigaction(SIGBUS, &oldact, NULL);
if (ret) {
/* Terminate QEMU since it can't recover from error */
perror("os_mem_prealloc: failed to reinstall signal handler");
exit(1);
}
pthread_sigmask(SIG_SETMASK, &oldset, NULL);
}


Expand Down
2 changes: 1 addition & 1 deletion util/oslib-win32.c
Expand Up @@ -539,7 +539,7 @@ int getpagesize(void)
return system_info.dwPageSize;
}

void os_mem_prealloc(int fd, char *area, size_t memory)
void os_mem_prealloc(int fd, char *area, size_t memory, Error **errp)
{
int i;
size_t pagesize = getpagesize();
Expand Down

0 comments on commit 056b68a

Please sign in to comment.