fix qemu exit on memory hotplug when allocation fails at prealloc time

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>
This commit is contained in:
Igor Mammedov 2016-07-20 11:54:03 +02:00 committed by Paolo Bonzini
parent 0b21757124
commit 056b68af77
5 changed files with 38 additions and 22 deletions

View File

@ -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, static void host_memory_backend_set_prealloc(Object *obj, bool value,
Error **errp) Error **errp)
{ {
Error *local_err = NULL;
HostMemoryBackend *backend = MEMORY_BACKEND(obj); HostMemoryBackend *backend = MEMORY_BACKEND(obj);
if (backend->force_prealloc) { if (backend->force_prealloc) {
@ -223,7 +224,11 @@ static void host_memory_backend_set_prealloc(Object *obj, bool value,
void *ptr = memory_region_get_ram_ptr(&backend->mr); void *ptr = memory_region_get_ram_ptr(&backend->mr);
uint64_t sz = memory_region_size(&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; backend->prealloc = true;
} }
} }
@ -286,8 +291,7 @@ host_memory_backend_memory_complete(UserCreatable *uc, Error **errp)
if (bc->alloc) { if (bc->alloc) {
bc->alloc(backend, &local_err); bc->alloc(backend, &local_err);
if (local_err) { if (local_err) {
error_propagate(errp, local_err); goto out;
return;
} }
ptr = memory_region_get_ram_ptr(&backend->mr); ptr = memory_region_get_ram_ptr(&backend->mr);
@ -343,9 +347,15 @@ host_memory_backend_memory_complete(UserCreatable *uc, Error **errp)
* specified NUMA policy in place. * specified NUMA policy in place.
*/ */
if (backend->prealloc) { 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 static bool

10
exec.c
View File

@ -1226,7 +1226,7 @@ static void *file_ram_alloc(RAMBlock *block,
char *filename; char *filename;
char *sanitized_name; char *sanitized_name;
char *c; char *c;
void *area; void *area = MAP_FAILED;
int fd = -1; int fd = -1;
int64_t page_size; int64_t page_size;
@ -1314,13 +1314,19 @@ static void *file_ram_alloc(RAMBlock *block,
} }
if (mem_prealloc) { if (mem_prealloc) {
os_mem_prealloc(fd, area, memory); os_mem_prealloc(fd, area, memory, errp);
if (errp && *errp) {
goto error;
}
} }
block->fd = fd; block->fd = fd;
return area; return area;
error: error:
if (area != MAP_FAILED) {
qemu_ram_munmap(area, memory);
}
if (unlink_on_error) { if (unlink_on_error) {
unlink(path); unlink(path);
} }

View File

@ -379,7 +379,7 @@ unsigned long qemu_getauxval(unsigned long type);
void qemu_set_tty_echo(int fd, bool echo); 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); int qemu_read_password(char *buf, int buf_size);

View File

@ -318,7 +318,7 @@ static void sigbus_handler(int signal)
siglongjmp(sigjump, 1); 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; int ret;
struct sigaction act, oldact; struct sigaction act, oldact;
@ -330,8 +330,9 @@ void os_mem_prealloc(int fd, char *area, size_t memory)
ret = sigaction(SIGBUS, &act, &oldact); ret = sigaction(SIGBUS, &act, &oldact);
if (ret) { if (ret) {
perror("os_mem_prealloc: failed to install signal handler"); error_setg_errno(errp, errno,
exit(1); "os_mem_prealloc: failed to install signal handler");
return;
} }
/* unblock SIGBUS */ /* unblock SIGBUS */
@ -340,9 +341,8 @@ void os_mem_prealloc(int fd, char *area, size_t memory)
pthread_sigmask(SIG_UNBLOCK, &set, &oldset); pthread_sigmask(SIG_UNBLOCK, &set, &oldset);
if (sigsetjmp(sigjump, 1)) { if (sigsetjmp(sigjump, 1)) {
fprintf(stderr, "os_mem_prealloc: Insufficient free host memory " error_setg(errp, "os_mem_prealloc: Insufficient free host memory "
"pages available to allocate guest RAM\n"); "pages available to allocate guest RAM\n");
exit(1);
} else { } else {
int i; int i;
size_t hpagesize = qemu_fd_getpagesize(fd); size_t hpagesize = qemu_fd_getpagesize(fd);
@ -352,15 +352,15 @@ void os_mem_prealloc(int fd, char *area, size_t memory)
for (i = 0; i < numpages; i++) { for (i = 0; i < numpages; i++) {
memset(area + (hpagesize * i), 0, 1); memset(area + (hpagesize * i), 0, 1);
} }
}
ret = sigaction(SIGBUS, &oldact, NULL); ret = sigaction(SIGBUS, &oldact, NULL);
if (ret) { if (ret) {
/* Terminate QEMU since it can't recover from error */
perror("os_mem_prealloc: failed to reinstall signal handler"); perror("os_mem_prealloc: failed to reinstall signal handler");
exit(1); exit(1);
} }
pthread_sigmask(SIG_SETMASK, &oldset, NULL); pthread_sigmask(SIG_SETMASK, &oldset, NULL);
}
} }

View File

@ -539,7 +539,7 @@ int getpagesize(void)
return system_info.dwPageSize; 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; int i;
size_t pagesize = getpagesize(); size_t pagesize = getpagesize();