*.tp
*.vr
*.d
+!scripts/qemu-guest-agent/fsfreeze-hook.d
*.o
*.lo
*.la
# avoid old build problems by removing potentially incorrect old files
rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h
rm -f qemu-options.def
- find . -name '*.[od]' -exec rm -f {} +
+ find . -name '*.[od]' -type f -exec rm -f {} +
rm -f *.a *.lo $(TOOLS) $(HELPERS-y) qemu-ga TAGS cscope.* *.pod *~ */*~
rm -Rf .libs
rm -f qemu-img-cmds.h
universal-obj-y += $(qom-obj-y)
+#######################################################################
+# Core hw code (qdev core)
+hw-core-obj-y += hw/
+hw-core-obj-y += qemu-option.o
+
+universal-obj-y += $(hw-core-obj-y)
+
#######################################################################
# oslib-obj-y is code depending on the OS (win32 vs posix)
oslib-obj-y = osdep.o cutils.o qemu-timer-common.o
user-obj-y \
common-obj-y \
universal-obj-y \
+ hw-core-obj-y \
extra-obj-y \
trace-obj-y
dummy := $(call unnest-vars)
+# core qdev-related obj files, also used by *-user:
+hw-core-obj-y += qdev.o qdev-properties.o
+# irq.o needed for qdev GPIO handling:
+hw-core-obj-y += irq.o
+
+
common-obj-y = usb/ ide/ pci/
common-obj-y += loader.o
common-obj-$(CONFIG_VIRTIO) += virtio-console.o
common-obj-$(CONFIG_REALLY_VIRTFS) += 9pfs/
common-obj-y += usb/
-common-obj-y += irq.o
common-obj-$(CONFIG_PTIMER) += ptimer.o
common-obj-$(CONFIG_MAX7310) += max7310.o
common-obj-$(CONFIG_WM8750) += wm8750.o
common-obj-y += bt.o bt-l2cap.o bt-sdp.o bt-hci.o bt-hid.o
common-obj-y += bt-hci-csr.o
common-obj-y += msmouse.o ps2.o
-common-obj-y += qdev.o qdev-properties.o qdev-monitor.o
+common-obj-y += qdev-monitor.o
common-obj-y += qdev-properties-system.o
common-obj-$(CONFIG_BRLAPI) += baum.o
klass->props = NULL;
}
-static void qdev_remove_from_bus(Object *obj)
+static void device_unparent(Object *obj)
{
DeviceState *dev = DEVICE(obj);
- bus_remove_child(dev->parent_bus, dev);
+ if (dev->parent_bus != NULL) {
+ bus_remove_child(dev->parent_bus, dev);
+ }
}
static void device_class_init(ObjectClass *class, void *data)
{
- class->unparent = qdev_remove_from_bus;
+ class->unparent = device_unparent;
}
void device_reset(DeviceState *dev)
};
#ifdef CONFIG_USER_ONLY
-#define qemu_init_vcpu(env) do { } while (0)
+static inline void qemu_init_vcpu(void *env)
+{
+}
#else
void qemu_init_vcpu(void *env);
#endif
#ifndef QEMU_CPU_H
#define QEMU_CPU_H
-#include "qom/object.h"
+#include "hw/qdev-core.h"
#include "qemu/thread.h"
/**
*/
typedef struct CPUClass {
/*< private >*/
- ObjectClass parent_class;
+ DeviceClass parent_class;
/*< public >*/
void (*reset)(CPUState *cpu);
*/
struct CPUState {
/*< private >*/
- Object parent_obj;
+ DeviceState parent_obj;
/*< public >*/
struct QemuThread *thread;
static int os_host_main_loop_wait(uint32_t timeout)
{
GMainContext *context = g_main_context_default();
- int ret, i;
+ int select_ret, g_poll_ret, ret, i;
PollingEntry *pe;
WaitObjects *w = &wait_objects;
gint poll_timeout;
return ret;
}
- if (nfds >= 0) {
- ret = select(nfds + 1, &rfds, &wfds, &xfds, &tv0);
- if (ret != 0) {
- timeout = 0;
- }
- }
-
g_main_context_prepare(context, &max_priority);
n_poll_fds = g_main_context_query(context, max_priority, &poll_timeout,
poll_fds, ARRAY_SIZE(poll_fds));
}
qemu_mutex_unlock_iothread();
- ret = g_poll(poll_fds, n_poll_fds + w->num, poll_timeout);
+ g_poll_ret = g_poll(poll_fds, n_poll_fds + w->num, poll_timeout);
qemu_mutex_lock_iothread();
- if (ret > 0) {
+ if (g_poll_ret > 0) {
for (i = 0; i < w->num; i++) {
w->revents[i] = poll_fds[n_poll_fds + i].revents;
}
g_main_context_dispatch(context);
}
- /* If an edge-triggered socket event occurred, select will return a
- * positive result on the next iteration. We do not need to do anything
- * here.
+ /* Call select after g_poll to avoid a useless iteration and therefore
+ * improve socket latency.
*/
- return ret;
+ if (nfds >= 0) {
+ select_ret = select(nfds + 1, &rfds, &wfds, &xfds, &tv0);
+ if (select_ret != 0) {
+ timeout = 0;
+ }
+ }
+
+ return select_ret || g_poll_ret;
}
#endif
#endif
#endif
+static void ga_wait_child(pid_t pid, int *status, Error **err)
+{
+ pid_t rpid;
+
+ *status = 0;
+
+ do {
+ rpid = waitpid(pid, status, 0);
+ } while (rpid == -1 && errno == EINTR);
+
+ if (rpid == -1) {
+ error_setg_errno(err, errno, "failed to wait for child (pid: %d)", pid);
+ return;
+ }
+
+ g_assert(rpid == pid);
+}
+
void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err)
{
const char *shutdown_flag;
- pid_t rpid, pid;
+ Error *local_err = NULL;
+ pid_t pid;
int status;
slog("guest-shutdown called, mode: %s", mode);
} else if (strcmp(mode, "reboot") == 0) {
shutdown_flag = "-r";
} else {
- error_set(err, QERR_INVALID_PARAMETER_VALUE, "mode",
- "halt|powerdown|reboot");
+ error_setg(err,
+ "mode is invalid (valid values are: halt|powerdown|reboot");
return;
}
"hypervisor initiated shutdown", (char*)NULL, environ);
_exit(EXIT_FAILURE);
} else if (pid < 0) {
- goto exit_err;
+ error_setg_errno(err, errno, "failed to create child process");
+ return;
}
- do {
- rpid = waitpid(pid, &status, 0);
- } while (rpid == -1 && errno == EINTR);
- if (rpid == pid && WIFEXITED(status) && !WEXITSTATUS(status)) {
+ ga_wait_child(pid, &status, &local_err);
+ if (error_is_set(&local_err)) {
+ error_propagate(err, local_err);
+ return;
+ }
+
+ if (!WIFEXITED(status)) {
+ error_setg(err, "child process has terminated abnormally");
+ return;
+ }
+
+ if (WEXITSTATUS(status)) {
+ error_setg(err, "child process has failed to shutdown");
return;
}
-exit_err:
- error_set(err, QERR_UNDEFINED_ERROR);
+ /* succeded */
}
typedef struct GuestFileHandle {
QTAILQ_INSERT_TAIL(&guest_file_state.filehandles, gfh, next);
}
-static GuestFileHandle *guest_file_handle_find(int64_t id)
+static GuestFileHandle *guest_file_handle_find(int64_t id, Error **err)
{
GuestFileHandle *gfh;
}
}
+ error_setg(err, "handle '%" PRId64 "' has not been found", id);
return NULL;
}
slog("guest-file-open called, filepath: %s, mode: %s", path, mode);
fh = fopen(path, mode);
if (!fh) {
- error_set(err, QERR_OPEN_FILE_FAILED, path);
+ error_setg_errno(err, errno, "failed to open file '%s' (mode: '%s')",
+ path, mode);
return -1;
}
ret = fcntl(fd, F_GETFL);
ret = fcntl(fd, F_SETFL, ret | O_NONBLOCK);
if (ret == -1) {
- error_set(err, QERR_QGA_COMMAND_FAILED, "fcntl() failed");
+ error_setg_errno(err, errno, "failed to make file '%s' non-blocking",
+ path);
fclose(fh);
return -1;
}
void qmp_guest_file_close(int64_t handle, Error **err)
{
- GuestFileHandle *gfh = guest_file_handle_find(handle);
+ GuestFileHandle *gfh = guest_file_handle_find(handle, err);
int ret;
slog("guest-file-close called, handle: %ld", handle);
if (!gfh) {
- error_set(err, QERR_FD_NOT_FOUND, "handle");
return;
}
ret = fclose(gfh->fh);
- if (ret == -1) {
- error_set(err, QERR_QGA_COMMAND_FAILED, "fclose() failed");
+ if (ret == EOF) {
+ error_setg_errno(err, errno, "failed to close handle");
return;
}
struct GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count,
int64_t count, Error **err)
{
- GuestFileHandle *gfh = guest_file_handle_find(handle);
+ GuestFileHandle *gfh = guest_file_handle_find(handle, err);
GuestFileRead *read_data = NULL;
guchar *buf;
FILE *fh;
size_t read_count;
if (!gfh) {
- error_set(err, QERR_FD_NOT_FOUND, "handle");
return NULL;
}
if (!has_count) {
count = QGA_READ_COUNT_DEFAULT;
} else if (count < 0) {
- error_set(err, QERR_INVALID_PARAMETER, "count");
+ error_setg(err, "value '%" PRId64 "' is invalid for argument count",
+ count);
return NULL;
}
buf = g_malloc0(count+1);
read_count = fread(buf, 1, count, fh);
if (ferror(fh)) {
+ error_setg_errno(err, errno, "failed to read file");
slog("guest-file-read failed, handle: %ld", handle);
- error_set(err, QERR_QGA_COMMAND_FAILED, "fread() failed");
} else {
buf[read_count] = 0;
read_data = g_malloc0(sizeof(GuestFileRead));
guchar *buf;
gsize buf_len;
int write_count;
- GuestFileHandle *gfh = guest_file_handle_find(handle);
+ GuestFileHandle *gfh = guest_file_handle_find(handle, err);
FILE *fh;
if (!gfh) {
- error_set(err, QERR_FD_NOT_FOUND, "handle");
return NULL;
}
if (!has_count) {
count = buf_len;
} else if (count < 0 || count > buf_len) {
+ error_setg(err, "value '%" PRId64 "' is invalid for argument count",
+ count);
g_free(buf);
- error_set(err, QERR_INVALID_PARAMETER, "count");
return NULL;
}
write_count = fwrite(buf, 1, count, fh);
if (ferror(fh)) {
+ error_setg_errno(err, errno, "failed to write to file");
slog("guest-file-write failed, handle: %ld", handle);
- error_set(err, QERR_QGA_COMMAND_FAILED, "fwrite() error");
} else {
write_data = g_malloc0(sizeof(GuestFileWrite));
write_data->count = write_count;
struct GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset,
int64_t whence, Error **err)
{
- GuestFileHandle *gfh = guest_file_handle_find(handle);
+ GuestFileHandle *gfh = guest_file_handle_find(handle, err);
GuestFileSeek *seek_data = NULL;
FILE *fh;
int ret;
if (!gfh) {
- error_set(err, QERR_FD_NOT_FOUND, "handle");
return NULL;
}
fh = gfh->fh;
ret = fseek(fh, offset, whence);
if (ret == -1) {
- error_set(err, QERR_QGA_COMMAND_FAILED, strerror(errno));
+ error_setg_errno(err, errno, "failed to seek file");
} else {
seek_data = g_malloc0(sizeof(GuestFileRead));
seek_data->position = ftell(fh);
void qmp_guest_file_flush(int64_t handle, Error **err)
{
- GuestFileHandle *gfh = guest_file_handle_find(handle);
+ GuestFileHandle *gfh = guest_file_handle_find(handle, err);
FILE *fh;
int ret;
if (!gfh) {
- error_set(err, QERR_FD_NOT_FOUND, "handle");
return;
}
fh = gfh->fh;
ret = fflush(fh);
if (ret == EOF) {
- error_set(err, QERR_QGA_COMMAND_FAILED, strerror(errno));
+ error_setg_errno(err, errno, "failed to flush file");
}
}
/*
* Walk the mount table and build a list of local file systems
*/
-static int build_fs_mount_list(FsMountList *mounts)
+static void build_fs_mount_list(FsMountList *mounts, Error **err)
{
struct mntent *ment;
FsMount *mount;
fp = setmntent(mtab, "r");
if (!fp) {
- g_warning("fsfreeze: unable to read mtab");
- return -1;
+ error_setg(err, "failed to open mtab file: '%s'", mtab);
+ return;
}
while ((ment = getmntent(fp))) {
}
endmntent(fp);
-
- return 0;
}
#endif
#if defined(CONFIG_FSFREEZE)
+typedef enum {
+ FSFREEZE_HOOK_THAW = 0,
+ FSFREEZE_HOOK_FREEZE,
+} FsfreezeHookArg;
+
+const char *fsfreeze_hook_arg_string[] = {
+ "thaw",
+ "freeze",
+};
+
+static void execute_fsfreeze_hook(FsfreezeHookArg arg, Error **err)
+{
+ int status;
+ pid_t pid;
+ const char *hook;
+ const char *arg_str = fsfreeze_hook_arg_string[arg];
+ Error *local_err = NULL;
+
+ hook = ga_fsfreeze_hook(ga_state);
+ if (!hook) {
+ return;
+ }
+ if (access(hook, X_OK) != 0) {
+ error_setg_errno(err, errno, "can't access fsfreeze hook '%s'", hook);
+ return;
+ }
+
+ slog("executing fsfreeze hook with arg '%s'", arg_str);
+ pid = fork();
+ if (pid == 0) {
+ setsid();
+ reopen_fd_to_null(0);
+ reopen_fd_to_null(1);
+ reopen_fd_to_null(2);
+
+ execle(hook, hook, arg_str, NULL, environ);
+ _exit(EXIT_FAILURE);
+ } else if (pid < 0) {
+ error_setg_errno(err, errno, "failed to create child process");
+ return;
+ }
+
+ ga_wait_child(pid, &status, &local_err);
+ if (error_is_set(&local_err)) {
+ error_propagate(err, local_err);
+ return;
+ }
+
+ if (!WIFEXITED(status)) {
+ error_setg(err, "fsfreeze hook has terminated abnormally");
+ return;
+ }
+
+ status = WEXITSTATUS(status);
+ if (status) {
+ error_setg(err, "fsfreeze hook has failed with status %d", status);
+ return;
+ }
+}
+
/*
* Return status of freeze/thaw
*/
int ret = 0, i = 0;
FsMountList mounts;
struct FsMount *mount;
+ Error *local_err = NULL;
int fd;
- char err_msg[512];
slog("guest-fsfreeze called");
+ execute_fsfreeze_hook(FSFREEZE_HOOK_FREEZE, &local_err);
+ if (error_is_set(&local_err)) {
+ error_propagate(err, local_err);
+ return -1;
+ }
+
QTAILQ_INIT(&mounts);
- ret = build_fs_mount_list(&mounts);
- if (ret < 0) {
- return ret;
+ build_fs_mount_list(&mounts, &local_err);
+ if (error_is_set(&local_err)) {
+ error_propagate(err, local_err);
+ return -1;
}
/* cannot risk guest agent blocking itself on a write in this state */
QTAILQ_FOREACH(mount, &mounts, next) {
fd = qemu_open(mount->dirname, O_RDONLY);
if (fd == -1) {
- sprintf(err_msg, "failed to open %s, %s", mount->dirname,
- strerror(errno));
- error_set(err, QERR_QGA_COMMAND_FAILED, err_msg);
+ error_setg_errno(err, errno, "failed to open %s", mount->dirname);
goto error;
}
ret = ioctl(fd, FIFREEZE);
if (ret == -1) {
if (errno != EOPNOTSUPP) {
- sprintf(err_msg, "failed to freeze %s, %s",
- mount->dirname, strerror(errno));
- error_set(err, QERR_QGA_COMMAND_FAILED, err_msg);
+ error_setg_errno(err, errno, "failed to freeze %s",
+ mount->dirname);
close(fd);
goto error;
}
FsMountList mounts;
FsMount *mount;
int fd, i = 0, logged;
+ Error *local_err = NULL;
QTAILQ_INIT(&mounts);
- ret = build_fs_mount_list(&mounts);
- if (ret) {
- error_set(err, QERR_QGA_COMMAND_FAILED,
- "failed to enumerate filesystems");
+ build_fs_mount_list(&mounts, &local_err);
+ if (error_is_set(&local_err)) {
+ error_propagate(err, local_err);
return 0;
}
ga_unset_frozen(ga_state);
free_fs_mount_list(&mounts);
+
+ execute_fsfreeze_hook(FSFREEZE_HOOK_THAW, err);
+
return i;
}
FsMountList mounts;
struct FsMount *mount;
int fd;
- char err_msg[512];
+ Error *local_err = NULL;
struct fstrim_range r = {
.start = 0,
.len = -1,
slog("guest-fstrim called");
QTAILQ_INIT(&mounts);
- ret = build_fs_mount_list(&mounts);
- if (ret < 0) {
+ build_fs_mount_list(&mounts, &local_err);
+ if (error_is_set(&local_err)) {
+ error_propagate(err, local_err);
return;
}
QTAILQ_FOREACH(mount, &mounts, next) {
fd = qemu_open(mount->dirname, O_RDONLY);
if (fd == -1) {
- sprintf(err_msg, "failed to open %s, %s", mount->dirname,
- strerror(errno));
- error_set(err, QERR_QGA_COMMAND_FAILED, err_msg);
+ error_setg_errno(err, errno, "failed to open %s", mount->dirname);
goto error;
}
ret = ioctl(fd, FITRIM, &r);
if (ret == -1) {
if (errno != ENOTTY && errno != EOPNOTSUPP) {
- sprintf(err_msg, "failed to trim %s, %s",
- mount->dirname, strerror(errno));
- error_set(err, QERR_QGA_COMMAND_FAILED, err_msg);
+ error_setg_errno(err, errno, "failed to trim %s",
+ mount->dirname);
close(fd);
goto error;
}
static void bios_supports_mode(const char *pmutils_bin, const char *pmutils_arg,
const char *sysfile_str, Error **err)
{
+ Error *local_err = NULL;
char *pmutils_path;
- pid_t pid, rpid;
+ pid_t pid;
int status;
pmutils_path = g_find_program_in_path(pmutils_bin);
}
_exit(SUSPEND_NOT_SUPPORTED);
+ } else if (pid < 0) {
+ error_setg_errno(err, errno, "failed to create child process");
+ goto out;
}
- g_free(pmutils_path);
+ ga_wait_child(pid, &status, &local_err);
+ if (error_is_set(&local_err)) {
+ error_propagate(err, local_err);
+ goto out;
+ }
- if (pid < 0) {
- goto undef_err;
+ if (!WIFEXITED(status)) {
+ error_setg(err, "child process has terminated abnormally");
+ goto out;
}
- do {
- rpid = waitpid(pid, &status, 0);
- } while (rpid == -1 && errno == EINTR);
- if (rpid == pid && WIFEXITED(status)) {
- switch (WEXITSTATUS(status)) {
- case SUSPEND_SUPPORTED:
- return;
- case SUSPEND_NOT_SUPPORTED:
- error_set(err, QERR_UNSUPPORTED);
- return;
- default:
- goto undef_err;
- }
+ switch (WEXITSTATUS(status)) {
+ case SUSPEND_SUPPORTED:
+ goto out;
+ case SUSPEND_NOT_SUPPORTED:
+ error_setg(err,
+ "the requested suspend mode is not supported by the guest");
+ goto out;
+ default:
+ error_setg(err,
+ "the helper program '%s' returned an unexpected exit status"
+ " code (%d)", pmutils_path, WEXITSTATUS(status));
+ goto out;
}
-undef_err:
- error_set(err, QERR_UNDEFINED_ERROR);
+out:
+ g_free(pmutils_path);
}
static void guest_suspend(const char *pmutils_bin, const char *sysfile_str,
Error **err)
{
+ Error *local_err = NULL;
char *pmutils_path;
- pid_t rpid, pid;
+ pid_t pid;
int status;
pmutils_path = g_find_program_in_path(pmutils_bin);
}
_exit(EXIT_SUCCESS);
+ } else if (pid < 0) {
+ error_setg_errno(err, errno, "failed to create child process");
+ goto out;
}
- g_free(pmutils_path);
+ ga_wait_child(pid, &status, &local_err);
+ if (error_is_set(&local_err)) {
+ error_propagate(err, local_err);
+ goto out;
+ }
- if (pid < 0) {
- goto exit_err;
+ if (!WIFEXITED(status)) {
+ error_setg(err, "child process has terminated abnormally");
+ goto out;
}
- do {
- rpid = waitpid(pid, &status, 0);
- } while (rpid == -1 && errno == EINTR);
- if (rpid == pid && WIFEXITED(status) && !WEXITSTATUS(status)) {
- return;
+ if (WEXITSTATUS(status)) {
+ error_setg(err, "child process has failed to suspend");
+ goto out;
}
-exit_err:
- error_set(err, QERR_UNDEFINED_ERROR);
+out:
+ g_free(pmutils_path);
}
void qmp_guest_suspend_disk(Error **err)
{
GuestNetworkInterfaceList *head = NULL, *cur_item = NULL;
struct ifaddrs *ifap, *ifa;
- char err_msg[512];
if (getifaddrs(&ifap) < 0) {
- snprintf(err_msg, sizeof(err_msg),
- "getifaddrs failed: %s", strerror(errno));
- error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
+ error_setg_errno(errp, errno, "getifaddrs failed");
goto error;
}
/* we haven't obtained HW address yet */
sock = socket(PF_INET, SOCK_STREAM, 0);
if (sock == -1) {
- snprintf(err_msg, sizeof(err_msg),
- "failed to create socket: %s", strerror(errno));
- error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
+ error_setg_errno(errp, errno, "failed to create socket");
goto error;
}
memset(&ifr, 0, sizeof(ifr));
pstrcpy(ifr.ifr_name, IF_NAMESIZE, info->value->name);
if (ioctl(sock, SIOCGIFHWADDR, &ifr) == -1) {
- snprintf(err_msg, sizeof(err_msg),
- "failed to get MAC address of %s: %s",
- ifa->ifa_name,
- strerror(errno));
- error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
+ error_setg_errno(errp, errno,
+ "failed to get MAC address of %s",
+ ifa->ifa_name);
goto error;
}
(int) mac_addr[0], (int) mac_addr[1],
(int) mac_addr[2], (int) mac_addr[3],
(int) mac_addr[4], (int) mac_addr[5]) == -1) {
- snprintf(err_msg, sizeof(err_msg),
- "failed to format MAC: %s", strerror(errno));
- error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
+ error_setg_errno(errp, errno, "failed to format MAC");
goto error;
}
address_item->value = g_malloc0(sizeof(*address_item->value));
p = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
if (!inet_ntop(AF_INET, p, addr4, sizeof(addr4))) {
- snprintf(err_msg, sizeof(err_msg),
- "inet_ntop failed : %s", strerror(errno));
- error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
+ error_setg_errno(errp, errno, "inet_ntop failed");
goto error;
}
address_item->value = g_malloc0(sizeof(*address_item->value));
p = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
if (!inet_ntop(AF_INET6, p, addr6, sizeof(addr6))) {
- snprintf(err_msg, sizeof(err_msg),
- "inet_ntop failed : %s", strerror(errno));
- error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
+ error_setg_errno(errp, errno, "inet_ntop failed");
goto error;
}
bool ga_is_frozen(GAState *s);
void ga_set_frozen(GAState *s);
void ga_unset_frozen(GAState *s);
+const char *ga_fsfreeze_hook(GAState *s);
#ifndef _WIN32
void reopen_fd_to_null(int fd);
#include "qga/service-win32.h"
#include <windows.h>
#endif
+#ifdef __linux__
+#include <linux/fs.h>
+#ifdef FIFREEZE
+#define CONFIG_FSFREEZE
+#endif
+#endif
#ifndef _WIN32
#define QGA_VIRTIO_PATH_DEFAULT "/dev/virtio-ports/org.qemu.guest_agent.0"
#endif
#define QGA_STATEDIR_DEFAULT CONFIG_QEMU_LOCALSTATEDIR "/run"
#define QGA_PIDFILE_DEFAULT QGA_STATEDIR_DEFAULT "/qemu-ga.pid"
+#ifdef CONFIG_FSFREEZE
+#define QGA_FSFREEZE_HOOK_DEFAULT CONFIG_QEMU_CONFDIR "/fsfreeze-hook"
+#endif
#define QGA_SENTINEL_BYTE 0xFF
struct GAState {
const char *log_filepath;
const char *pid_filepath;
} deferred_options;
+#ifdef CONFIG_FSFREEZE
+ const char *fsfreeze_hook;
+#endif
};
struct GAState *ga_state;
" %s)\n"
" -l, --logfile set logfile path, logs to stderr by default\n"
" -f, --pidfile specify pidfile (default is %s)\n"
+#ifdef CONFIG_FSFREEZE
+" -F, --fsfreeze-hook\n"
+" enable fsfreeze hook. Accepts an optional argument that\n"
+" specifies script to run on freeze/thaw. Script will be\n"
+" called with 'freeze'/'thaw' arguments accordingly.\n"
+" (default is %s)\n"
+" If using -F with an argument, do not follow -F with a\n"
+" space.\n"
+" (for example: -F/var/run/fsfreezehook.sh)\n"
+#endif
" -t, --statedir specify dir to store state information (absolute paths\n"
" only, default is %s)\n"
" -v, --verbose log extra debugging information\n"
"\n"
"Report bugs to <mdroth@linux.vnet.ibm.com>\n"
, cmd, QEMU_VERSION, QGA_VIRTIO_PATH_DEFAULT, QGA_PIDFILE_DEFAULT,
+#ifdef CONFIG_FSFREEZE
+ QGA_FSFREEZE_HOOK_DEFAULT,
+#endif
QGA_STATEDIR_DEFAULT);
}
}
}
+#ifdef CONFIG_FSFREEZE
+const char *ga_fsfreeze_hook(GAState *s)
+{
+ return s->fsfreeze_hook;
+}
+#endif
+
static void become_daemon(const char *pidfile)
{
#ifndef _WIN32
int main(int argc, char **argv)
{
- const char *sopt = "hVvdm:p:l:f:b:s:t:";
+ const char *sopt = "hVvdm:p:l:f:F::b:s:t:";
const char *method = NULL, *path = NULL;
const char *log_filepath = NULL;
const char *pid_filepath = QGA_PIDFILE_DEFAULT;
+#ifdef CONFIG_FSFREEZE
+ const char *fsfreeze_hook = NULL;
+#endif
const char *state_dir = QGA_STATEDIR_DEFAULT;
#ifdef _WIN32
const char *service = NULL;
{ "version", 0, NULL, 'V' },
{ "logfile", 1, NULL, 'l' },
{ "pidfile", 1, NULL, 'f' },
+#ifdef CONFIG_FSFREEZE
+ { "fsfreeze-hook", 2, NULL, 'F' },
+#endif
{ "verbose", 0, NULL, 'v' },
{ "method", 1, NULL, 'm' },
{ "path", 1, NULL, 'p' },
case 'f':
pid_filepath = optarg;
break;
+#ifdef CONFIG_FSFREEZE
+ case 'F':
+ fsfreeze_hook = optarg ? optarg : QGA_FSFREEZE_HOOK_DEFAULT;
+ break;
+#endif
case 't':
state_dir = optarg;
break;
s = g_malloc0(sizeof(GAState));
s->log_level = log_level;
s->log_file = stderr;
+#ifdef CONFIG_FSFREEZE
+ s->fsfreeze_hook = fsfreeze_hook;
+#endif
g_log_set_default_handler(ga_log, s);
g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR);
ga_enable_logging(s);
static void cpu_class_init(ObjectClass *klass, void *data)
{
+ DeviceClass *dc = DEVICE_CLASS(klass);
CPUClass *k = CPU_CLASS(klass);
k->reset = cpu_common_reset;
+ dc->no_user = 1;
}
-static TypeInfo cpu_type_info = {
+static const TypeInfo cpu_type_info = {
.name = TYPE_CPU,
- .parent = TYPE_OBJECT,
+ .parent = TYPE_DEVICE,
.instance_size = sizeof(CPUState),
.abstract = true,
.class_size = sizeof(CPUClass),
--- /dev/null
+#!/bin/sh
+
+# This script is executed when a guest agent receives fsfreeze-freeze and
+# fsfreeze-thaw command, if it is specified in --fsfreeze-hook (-F)
+# option of qemu-ga or placed in default path (/etc/qemu/fsfreeze-hook).
+# When the agent receives fsfreeze-freeze request, this script is issued with
+# "freeze" argument before the filesystem is frozen. And for fsfreeze-thaw
+# request, it is issued with "thaw" argument after filesystem is thawed.
+
+LOGFILE=/var/log/qga-fsfreeze-hook.log
+FSFREEZE_D=$(dirname -- "$0")/fsfreeze-hook.d
+
+# Check whether file $1 is a backup or rpm-generated file and should be ignored
+is_ignored_file() {
+ case "$1" in
+ *~ | *.bak | *.orig | *.rpmnew | *.rpmorig | *.rpmsave | *.sample)
+ return 0 ;;
+ esac
+ return 1
+}
+
+# Iterate executables in directory "fsfreeze-hook.d" with the specified args
+[ ! -d "$FSFREEZE_D" ] && exit 0
+for file in "$FSFREEZE_D"/* ; do
+ is_ignored_file "$file" && continue
+ [ -x "$file" ] || continue
+ printf "$(date): execute $file $@\n" >>$LOGFILE
+ "$file" "$@" >>$LOGFILE 2>&1
+ STATUS=$?
+ printf "$(date): $file finished with status=$STATUS\n" >>$LOGFILE
+done
+
+exit 0
--- /dev/null
+#!/bin/sh
+
+# Flush MySQL tables to the disk before the filesystem is frozen.
+# At the same time, this keeps a read lock in order to avoid write accesses
+# from the other clients until the filesystem is thawed.
+
+MYSQL="/usr/bin/mysql"
+MYSQL_OPTS="-uroot" #"-prootpassword"
+FIFO=/var/run/mysql-flush.fifo
+
+# Check mysql is installed and the server running
+[ -x "$MYSQL" ] && "$MYSQL" $MYSQL_OPTS < /dev/null || exit 0
+
+flush_and_wait() {
+ printf "FLUSH TABLES WITH READ LOCK \\G\n"
+ trap 'printf "$(date): $0 is killed\n">&2' HUP INT QUIT ALRM TERM
+ read < $FIFO
+ printf "UNLOCK TABLES \\G\n"
+ rm -f $FIFO
+}
+
+case "$1" in
+ freeze)
+ mkfifo $FIFO || exit 1
+ flush_and_wait | "$MYSQL" $MYSQL_OPTS &
+ # wait until every block is flushed
+ while [ "$(echo 'SHOW STATUS LIKE "Key_blocks_not_flushed"' |\
+ "$MYSQL" $MYSQL_OPTS | tail -1 | cut -f 2)" -gt 0 ]; do
+ sleep 1
+ done
+ # for InnoDB, wait until every log is flushed
+ INNODB_STATUS=$(mktemp /tmp/mysql-flush.XXXXXX)
+ [ $? -ne 0 ] && exit 2
+ trap "rm -f $INNODB_STATUS; exit 1" HUP INT QUIT ALRM TERM
+ while :; do
+ printf "SHOW ENGINE INNODB STATUS \\G" |\
+ "$MYSQL" $MYSQL_OPTS > $INNODB_STATUS
+ LOG_CURRENT=$(grep 'Log sequence number' $INNODB_STATUS |\
+ tr -s ' ' | cut -d' ' -f4)
+ LOG_FLUSHED=$(grep 'Log flushed up to' $INNODB_STATUS |\
+ tr -s ' ' | cut -d' ' -f5)
+ [ "$LOG_CURRENT" = "$LOG_FLUSHED" ] && break
+ sleep 1
+ done
+ rm -f $INNODB_STATUS
+ ;;
+
+ thaw)
+ [ ! -p $FIFO ] && exit 1
+ echo > $FIFO
+ ;;
+
+ *)
+ exit 1
+ ;;
+esac
stub-obj-y += fdset-remove-fd.o
stub-obj-y += get-fd.o
stub-obj-y += set-fd-handler.o
+stub-obj-y += reset.o
+stub-obj-y += vmstate.o
+stub-obj-y += sysbus.o
stub-obj-$(CONFIG_WIN32) += fd-register.o
--- /dev/null
+#include "hw/hw.h"
+
+/* Stub functions for binaries that never call qemu_devices_reset(),
+ * and don't need to keep track of the reset handler list.
+ */
+
+void qemu_register_reset(QEMUResetHandler *func, void *opaque)
+{
+}
+
+void qemu_unregister_reset(QEMUResetHandler *func, void *opaque)
+{
+}
--- /dev/null
+#include "hw/qdev-core.h"
+
+BusState *sysbus_get_default(void)
+{
+ return NULL;
+}
--- /dev/null
+#include "qemu-common.h"
+#include "migration/vmstate.h"
+
+int vmstate_register_with_alias_id(DeviceState *dev,
+ int instance_id,
+ const VMStateDescription *vmsd,
+ void *base, int alias_id,
+ int required_for_version)
+{
+ return 0;
+}
+
+void vmstate_unregister(DeviceState *dev,
+ const VMStateDescription *vmsd,
+ void *opaque)
+{
+}
static void alpha_cpu_realize(Object *obj, Error **errp)
{
-#ifndef CONFIG_USER_ONLY
AlphaCPU *cpu = ALPHA_CPU(obj);
qemu_init_vcpu(&cpu->env);
-#endif
}
/* Sort alphabetically by type name. */
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
};
+const char *get_register_name_32(unsigned int reg)
+{
+ static const char *reg_names[CPU_NB_REGS32] = {
+ [R_EAX] = "EAX",
+ [R_ECX] = "ECX",
+ [R_EDX] = "EDX",
+ [R_EBX] = "EBX",
+ [R_ESP] = "ESP",
+ [R_EBP] = "EBP",
+ [R_ESI] = "ESI",
+ [R_EDI] = "EDI",
+ };
+
+ if (reg > CPU_NB_REGS32) {
+ return NULL;
+ }
+ return reg_names[reg];
+}
+
/* collects per-function cpuid data
*/
typedef struct model_features_t {
uint32_t *guest_feat;
uint32_t *host_feat;
- uint32_t check_feat;
const char **flag_names;
uint32_t cpuid;
- } model_features_t;
+ int reg;
+} model_features_t;
int check_cpuid = 0;
int enforce_cpuid = 0;
{
.name = "core2duo",
.level = 10,
+ .vendor1 = CPUID_VENDOR_INTEL_1,
+ .vendor2 = CPUID_VENDOR_INTEL_2,
+ .vendor3 = CPUID_VENDOR_INTEL_3,
.family = 6,
.model = 15,
.stepping = 11,
{
.name = "qemu32",
.level = 4,
+ .vendor1 = CPUID_VENDOR_INTEL_1,
+ .vendor2 = CPUID_VENDOR_INTEL_2,
+ .vendor3 = CPUID_VENDOR_INTEL_3,
.family = 6,
.model = 3,
.stepping = 3,
{
.name = "kvm32",
.level = 5,
+ .vendor1 = CPUID_VENDOR_INTEL_1,
+ .vendor2 = CPUID_VENDOR_INTEL_2,
+ .vendor3 = CPUID_VENDOR_INTEL_3,
.family = 15,
.model = 6,
.stepping = 1,
{
.name = "coreduo",
.level = 10,
+ .vendor1 = CPUID_VENDOR_INTEL_1,
+ .vendor2 = CPUID_VENDOR_INTEL_2,
+ .vendor3 = CPUID_VENDOR_INTEL_3,
.family = 6,
.model = 14,
.stepping = 8,
{
.name = "486",
.level = 1,
+ .vendor1 = CPUID_VENDOR_INTEL_1,
+ .vendor2 = CPUID_VENDOR_INTEL_2,
+ .vendor3 = CPUID_VENDOR_INTEL_3,
.family = 4,
.model = 0,
.stepping = 0,
{
.name = "pentium",
.level = 1,
+ .vendor1 = CPUID_VENDOR_INTEL_1,
+ .vendor2 = CPUID_VENDOR_INTEL_2,
+ .vendor3 = CPUID_VENDOR_INTEL_3,
.family = 5,
.model = 4,
.stepping = 3,
{
.name = "pentium2",
.level = 2,
+ .vendor1 = CPUID_VENDOR_INTEL_1,
+ .vendor2 = CPUID_VENDOR_INTEL_2,
+ .vendor3 = CPUID_VENDOR_INTEL_3,
.family = 6,
.model = 5,
.stepping = 2,
{
.name = "pentium3",
.level = 2,
+ .vendor1 = CPUID_VENDOR_INTEL_1,
+ .vendor2 = CPUID_VENDOR_INTEL_2,
+ .vendor3 = CPUID_VENDOR_INTEL_3,
.family = 6,
.model = 7,
.stepping = 3,
.name = "n270",
/* original is on level 10 */
.level = 5,
+ .vendor1 = CPUID_VENDOR_INTEL_1,
+ .vendor2 = CPUID_VENDOR_INTEL_2,
+ .vendor3 = CPUID_VENDOR_INTEL_3,
.family = 6,
.model = 28,
.stepping = 2,
}
}
- /*
- * Every SVM feature requires emulation support in KVM - so we can't just
- * read the host features here. KVM might even support SVM features not
- * available on the host hardware. Just set all bits and mask out the
- * unsupported ones later.
- */
- x86_cpu_def->svm_features = -1;
+ /* Other KVM-specific feature fields: */
+ x86_cpu_def->svm_features =
+ kvm_arch_get_supported_cpuid(s, 0x8000000A, 0, R_EDX);
+ x86_cpu_def->kvm_features =
+ kvm_arch_get_supported_cpuid(s, KVM_CPUID_FEATURES, 0, R_EAX);
+
#endif /* CONFIG_KVM */
}
for (i = 0; i < 32; ++i)
if (1 << i & mask) {
- fprintf(stderr, "warning: host cpuid %04x_%04x lacks requested"
- " flag '%s' [0x%08x]\n",
- f->cpuid >> 16, f->cpuid & 0xffff,
- f->flag_names[i] ? f->flag_names[i] : "[reserved]", mask);
+ const char *reg = get_register_name_32(f->reg);
+ assert(reg);
+ fprintf(stderr, "warning: host doesn't support requested feature: "
+ "CPUID.%02XH:%s%s%s [bit %d]\n",
+ f->cpuid, reg,
+ f->flag_names[i] ? "." : "",
+ f->flag_names[i] ? f->flag_names[i] : "", i);
break;
}
return 0;
}
/* best effort attempt to inform user requested cpu flags aren't making
- * their way to the guest. Note: ft[].check_feat ideally should be
- * specified via a guest_def field to suppress report of extraneous flags.
+ * their way to the guest.
*
* This function may be called only if KVM is enabled.
*/
int rv, i;
struct model_features_t ft[] = {
{&guest_def->features, &host_def.features,
- ~0, feature_name, 0x00000000},
+ feature_name, 0x00000001, R_EDX},
{&guest_def->ext_features, &host_def.ext_features,
- ~CPUID_EXT_HYPERVISOR, ext_feature_name, 0x00000001},
+ ext_feature_name, 0x00000001, R_ECX},
{&guest_def->ext2_features, &host_def.ext2_features,
- ~PPRO_FEATURES, ext2_feature_name, 0x80000000},
+ ext2_feature_name, 0x80000001, R_EDX},
{&guest_def->ext3_features, &host_def.ext3_features,
- ~CPUID_EXT3_SVM, ext3_feature_name, 0x80000001}};
+ ext3_feature_name, 0x80000001, R_ECX}
+ };
assert(kvm_enabled());
kvm_cpu_fill_host(&host_def);
for (rv = 0, i = 0; i < ARRAY_SIZE(ft); ++i)
for (mask = 1; mask; mask <<= 1)
- if (ft[i].check_feat & mask && *ft[i].guest_feat & mask &&
+ if (*ft[i].guest_feat & mask &&
!(*ft[i].host_feat & mask)) {
unavailable_host_feature(&ft[i], mask);
rv = 1;
if (cpu_x86_parse_featurestr(def, features) < 0) {
goto error;
}
- if (def->vendor1) {
- env->cpuid_vendor1 = def->vendor1;
- env->cpuid_vendor2 = def->vendor2;
- env->cpuid_vendor3 = def->vendor3;
- } else {
- env->cpuid_vendor1 = CPUID_VENDOR_INTEL_1;
- env->cpuid_vendor2 = CPUID_VENDOR_INTEL_2;
- env->cpuid_vendor3 = CPUID_VENDOR_INTEL_3;
- }
+ assert(def->vendor1);
+ env->cpuid_vendor1 = def->vendor1;
+ env->cpuid_vendor2 = def->vendor2;
+ env->cpuid_vendor3 = def->vendor3;
env->cpuid_vendor_override = def->vendor_override;
object_property_set_int(OBJECT(cpu), def->level, "level", &error);
object_property_set_int(OBJECT(cpu), def->family, "family", &error);
object_property_set_int(OBJECT(cpu), (int64_t)def->tsc_khz * 1000,
"tsc-frequency", &error);
- /* On AMD CPUs, some CPUID[8000_0001].EDX bits must match the bits on
- * CPUID[1].EDX.
- */
- if (env->cpuid_vendor1 == CPUID_VENDOR_AMD_1 &&
- env->cpuid_vendor2 == CPUID_VENDOR_AMD_2 &&
- env->cpuid_vendor3 == CPUID_VENDOR_AMD_3) {
- env->cpuid_ext2_features &= ~CPUID_EXT2_AMD_ALIASES;
- env->cpuid_ext2_features |= (def->features & CPUID_EXT2_AMD_ALIASES);
- }
-
- if (!kvm_enabled()) {
- env->cpuid_features &= TCG_FEATURES;
- env->cpuid_ext_features &= TCG_EXT_FEATURES;
- env->cpuid_ext2_features &= (TCG_EXT2_FEATURES
-#ifdef TARGET_X86_64
- | CPUID_EXT2_SYSCALL | CPUID_EXT2_LM
-#endif
- );
- env->cpuid_ext3_features &= TCG_EXT3_FEATURES;
- env->cpuid_svm_features &= TCG_SVM_FEATURES;
- } else {
-#ifdef CONFIG_KVM
- filter_features_for_kvm(cpu);
-#endif
- }
object_property_set_str(OBJECT(cpu), def->model_id, "model-id", &error);
if (error) {
fprintf(stderr, "%s\n", error_get_pretty(error));
env->cpuid_level = 7;
}
+ /* On AMD CPUs, some CPUID[8000_0001].EDX bits must match the bits on
+ * CPUID[1].EDX.
+ */
+ if (env->cpuid_vendor1 == CPUID_VENDOR_AMD_1 &&
+ env->cpuid_vendor2 == CPUID_VENDOR_AMD_2 &&
+ env->cpuid_vendor3 == CPUID_VENDOR_AMD_3) {
+ env->cpuid_ext2_features &= ~CPUID_EXT2_AMD_ALIASES;
+ env->cpuid_ext2_features |= (env->cpuid_features
+ & CPUID_EXT2_AMD_ALIASES);
+ }
+
+ if (!kvm_enabled()) {
+ env->cpuid_features &= TCG_FEATURES;
+ env->cpuid_ext_features &= TCG_EXT_FEATURES;
+ env->cpuid_ext2_features &= (TCG_EXT2_FEATURES
+#ifdef TARGET_X86_64
+ | CPUID_EXT2_SYSCALL | CPUID_EXT2_LM
+#endif
+ );
+ env->cpuid_ext3_features &= TCG_EXT3_FEATURES;
+ env->cpuid_svm_features &= TCG_SVM_FEATURES;
+ } else {
+#ifdef CONFIG_KVM
+ filter_features_for_kvm(cpu);
+#endif
+ }
+
#ifndef CONFIG_USER_ONLY
qemu_register_reset(x86_cpu_machine_reset_cb, cpu);
void enable_kvm_pv_eoi(void);
+/* Return name of 32-bit register, from a R_* constant */
+const char *get_register_name_32(unsigned int reg);
+
#endif /* CPU_I386_H */