Skip to content

Commit

Permalink
qga: implement qmp_guest_get_memory_blocks() for Linux with sysfs
Browse files Browse the repository at this point in the history
We can get guest's memory block information by using command
"guest-get-memory-blocks", the returned value contains a list of memory block
info, such as phys-index, online state, can-offline info.

Signed-off-by: zhanghailiang <zhang.zhanghailiang@huawei.com>

*replaced guest-triggerable assertion with an error msg

Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
  • Loading branch information
colo-ft authored and mdroth committed Feb 17, 2015
1 parent a065aaa commit bd240fc
Showing 1 changed file with 232 additions and 1 deletion.
233 changes: 232 additions & 1 deletion qga/commands-posix.c
Original file line number Diff line number Diff line change
Expand Up @@ -1992,9 +1992,240 @@ void qmp_guest_set_user_password(const char *username,
}
}

static void ga_read_sysfs_file(int dirfd, const char *pathname, char *buf,
int size, Error **errp)
{
int fd;
int res;

errno = 0;
fd = openat(dirfd, pathname, O_RDONLY);
if (fd == -1) {
error_setg_errno(errp, errno, "open sysfs file \"%s\"", pathname);
return;
}

res = pread(fd, buf, size, 0);
if (res == -1) {
error_setg_errno(errp, errno, "pread sysfs file \"%s\"", pathname);
} else if (res == 0) {
error_setg(errp, "pread sysfs file \"%s\": unexpected EOF", pathname);
}
close(fd);
}

static void ga_write_sysfs_file(int dirfd, const char *pathname,
const char *buf, int size, Error **errp)
{
int fd;

errno = 0;
fd = openat(dirfd, pathname, O_WRONLY);
if (fd == -1) {
error_setg_errno(errp, errno, "open sysfs file \"%s\"", pathname);
return;
}

if (pwrite(fd, buf, size, 0) == -1) {
error_setg_errno(errp, errno, "pwrite sysfs file \"%s\"", pathname);
}

close(fd);
}

/* Transfer online/offline status between @mem_blk and the guest system.
*
* On input either @errp or *@errp must be NULL.
*
* In system-to-@mem_blk direction, the following @mem_blk fields are accessed:
* - R: mem_blk->phys_index
* - W: mem_blk->online
* - W: mem_blk->can_offline
*
* In @mem_blk-to-system direction, the following @mem_blk fields are accessed:
* - R: mem_blk->phys_index
* - R: mem_blk->online
*- R: mem_blk->can_offline
* Written members remain unmodified on error.
*/
static void transfer_memory_block(GuestMemoryBlock *mem_blk, bool sys2memblk,
GuestMemoryBlockResponse *result,
Error **errp)
{
char *dirpath;
int dirfd;
char *status;
Error *local_err = NULL;

if (!sys2memblk) {
DIR *dp;

if (!result) {
error_setg(errp, "Internal error, 'result' should not be NULL");
return;
}
errno = 0;
dp = opendir("/sys/devices/system/memory/");
/* if there is no 'memory' directory in sysfs,
* we think this VM does not support online/offline memory block,
* any other solution?
*/
if (!dp && errno == ENOENT) {
result->response =
GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_NOT_SUPPORTED;
goto out1;
}
closedir(dp);
}

dirpath = g_strdup_printf("/sys/devices/system/memory/memory%" PRId64 "/",
mem_blk->phys_index);
dirfd = open(dirpath, O_RDONLY | O_DIRECTORY);
if (dirfd == -1) {
if (sys2memblk) {
error_setg_errno(errp, errno, "open(\"%s\")", dirpath);
} else {
if (errno == ENOENT) {
result->response = GUEST_MEMORY_BLOCK_RESPONSE_TYPE_NOT_FOUND;
} else {
result->response =
GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED;
}
}
g_free(dirpath);
goto out1;
}
g_free(dirpath);

status = g_malloc0(10);
ga_read_sysfs_file(dirfd, "state", status, 10, &local_err);
if (local_err) {
/* treat with sysfs file that not exist in old kernel */
if (errno == ENOENT) {
error_free(local_err);
if (sys2memblk) {
mem_blk->online = true;
mem_blk->can_offline = false;
} else if (!mem_blk->online) {
result->response =
GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_NOT_SUPPORTED;
}
} else {
if (sys2memblk) {
error_propagate(errp, local_err);
} else {
result->response =
GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED;
}
}
goto out2;
}

if (sys2memblk) {
char removable = '0';

mem_blk->online = (strncmp(status, "online", 6) == 0);

ga_read_sysfs_file(dirfd, "removable", &removable, 1, &local_err);
if (local_err) {
/* if no 'removable' file, it does't support offline mem blk */
if (errno == ENOENT) {
error_free(local_err);
mem_blk->can_offline = false;
} else {
error_propagate(errp, local_err);
}
} else {
mem_blk->can_offline = (removable != '0');
}
} else {
if (mem_blk->online != (strncmp(status, "online", 6) == 0)) {
char *new_state = mem_blk->online ? g_strdup("online") :
g_strdup("offline");

ga_write_sysfs_file(dirfd, "state", new_state, strlen(new_state),
&local_err);
g_free(new_state);
if (local_err) {
error_free(local_err);
result->response =
GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED;
goto out2;
}

result->response = GUEST_MEMORY_BLOCK_RESPONSE_TYPE_SUCCESS;
result->has_error_code = false;
} /* otherwise pretend successful re-(on|off)-lining */
}
g_free(status);
close(dirfd);
return;

out2:
g_free(status);
close(dirfd);
out1:
if (!sys2memblk) {
result->has_error_code = true;
result->error_code = errno;
}
}

GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp)
{
error_set(errp, QERR_UNSUPPORTED);
GuestMemoryBlockList *head, **link;
Error *local_err = NULL;
struct dirent *de;
DIR *dp;

head = NULL;
link = &head;

dp = opendir("/sys/devices/system/memory/");
if (!dp) {
error_setg_errno(errp, errno, "Can't open directory"
"\"/sys/devices/system/memory/\"\n");
return NULL;
}

/* Note: the phys_index of memory block may be discontinuous,
* this is because a memblk is the unit of the Sparse Memory design, which
* allows discontinuous memory ranges (ex. NUMA), so here we should
* traverse the memory block directory.
*/
while ((de = readdir(dp)) != NULL) {
GuestMemoryBlock *mem_blk;
GuestMemoryBlockList *entry;

if ((strncmp(de->d_name, "memory", 6) != 0) ||
!(de->d_type & DT_DIR)) {
continue;
}

mem_blk = g_malloc0(sizeof *mem_blk);
/* The d_name is "memoryXXX", phys_index is block id, same as XXX */
mem_blk->phys_index = strtoul(&de->d_name[6], NULL, 10);
mem_blk->has_can_offline = true; /* lolspeak ftw */
transfer_memory_block(mem_blk, true, NULL, &local_err);

entry = g_malloc0(sizeof *entry);
entry->value = mem_blk;

*link = entry;
link = &entry->next;
}

closedir(dp);
if (local_err == NULL) {
/* there's no guest with zero memory blocks */
if (head == NULL) {
error_setg(errp, "guest reported zero memory blocks!");
}
return head;
}

qapi_free_GuestMemoryBlockList(head);
error_propagate(errp, local_err);
return NULL;
}

Expand Down

0 comments on commit bd240fc

Please sign in to comment.