We already provide ways to detect when a domain has been paused as a result of I/O error, but there was no way of getting the exact error or even the device that experienced it. This new API may be used for both. --- daemon/remote.c | 40 ++++++++++++++++++++++++++++++++ include/libvirt/libvirt.h.in | 32 +++++++++++++++++++++++++ python/generator.py | 3 +- src/driver.h | 7 +++++ src/libvirt.c | 52 ++++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 1 + src/remote/remote_driver.c | 34 +++++++++++++++++++++++++++ src/remote/remote_protocol.x | 22 +++++++++++++++++- src/remote_protocol-structs | 16 +++++++++++++ src/rpc/gendispatch.pl | 47 +++++++++++++++++++++++++++++++++++++ 10 files changed, 252 insertions(+), 2 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index cb8423a..31bf1de 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -98,6 +98,12 @@ remoteDeserializeTypedParameters(remote_typed_param *args_params_val, int limit, int *nparams); +static int +remoteSerializeDomainDiskErrors(virDomainDiskErrorPtr errors, + int nerrors, + remote_domain_disk_error **ret_errors_val, + u_int *ret_errors_len); + #include "remote_dispatch.h" #include "qemu_dispatch.h" @@ -3725,3 +3731,37 @@ make_nonnull_domain_snapshot(remote_nonnull_domain_snapshot *snapshot_dst, virDo snapshot_dst->name = strdup(snapshot_src->name); make_nonnull_domain(&snapshot_dst->dom, snapshot_src->domain); } + +static int +remoteSerializeDomainDiskErrors(virDomainDiskErrorPtr errors, + int nerrors, + remote_domain_disk_error **ret_errors_val, + u_int *ret_errors_len) +{ + remote_domain_disk_error *val = NULL; + int i = 0; + + if (VIR_ALLOC_N(val, nerrors) < 0) + goto no_memory; + + for (i = 0; i < nerrors; i++) { + if (!(val[i].disk = strdup(errors[i].disk))) + goto no_memory; + val[i].error = errors[i].error; + } + + *ret_errors_len = nerrors; + *ret_errors_val = val; + + return 0; + +no_memory: + if (val) { + int j; + for (j = 0; j < i; j++) + VIR_FREE(val[j].disk); + VIR_FREE(val); + } + virReportOOMError(); + return -1; +} diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 0a7b324..272d142 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1967,6 +1967,38 @@ virDomainGetBlockIoTune(virDomainPtr dom, int *nparams, unsigned int flags); +/** + * virDomainDiskErrorCode: + * + * Disk I/O error. + */ +typedef enum { + VIR_DOMAIN_DISK_ERROR_NONE = 0, /* no error */ + VIR_DOMAIN_DISK_ERROR_UNSPEC = 1, /* unspecified I/O error */ + VIR_DOMAIN_DISK_ERROR_NO_SPACE = 2, /* no space left on the device */ + +#ifdef VIR_ENUM_SENTINELS + VIR_DOMAIN_DISK_ERROR_LAST +#endif +} virDomainDiskErrorCode; + +/** + * virDomainDiskError: + * + */ +typedef struct _virDomainDiskError virDomainDiskError; +typedef virDomainDiskError *virDomainDiskErrorPtr; + +struct _virDomainDiskError { + char *disk; /* disk target */ + int error; /* virDomainDiskErrorCode */ +}; + +int virDomainGetDiskErrors(virDomainPtr dom, + virDomainDiskErrorPtr errors, + int maxerrors, + unsigned int flags); + /* * NUMA support diff --git a/python/generator.py b/python/generator.py index 6f813ae..b514af5 100755 --- a/python/generator.py +++ b/python/generator.py @@ -423,7 +423,8 @@ skip_impl = ( 'virDomainGetBlockIoTune', 'virDomainSetInterfaceParameters', 'virDomainGetInterfaceParameters', - 'virDomainGetCPUStats' # not implemented now. + 'virDomainGetCPUStats', # not implemented now. + 'virDomainGetDiskErrors', ) qemu_skip_impl = ( diff --git a/src/driver.h b/src/driver.h index ba7dbc4..f3ded19 100644 --- a/src/driver.h +++ b/src/driver.h @@ -810,6 +810,12 @@ typedef int unsigned int ncpus, unsigned int flags); +typedef int + (*virDrvDomainGetDiskErrors)(virDomainPtr dom, + virDomainDiskErrorPtr errors, + int maxerrors, + unsigned int flags); + /** * _virDriver: * @@ -981,6 +987,7 @@ struct _virDriver { virDrvDomainSetBlockIoTune domainSetBlockIoTune; virDrvDomainGetBlockIoTune domainGetBlockIoTune; virDrvDomainGetCPUStats domainGetCPUStats; + virDrvDomainGetDiskErrors domainGetDiskErrors; }; typedef int diff --git a/src/libvirt.c b/src/libvirt.c index e702a34..345c95b 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -18281,3 +18281,55 @@ error: virDispatchError(domain->conn); return -1; } + +/** + * virDomainGetDiskErrors: + * @dom: a domain object + * @errors: array to populate on output + * @maxerrors: size of @errors array + * @flags: extra flags; not used yet, so callers should always pass 0 + * + * The function populates @errors array with all disks that encountered an + * I/O error. Each disk is identified by its target (the dev attribute of + * target subelement in domain XML), such as "vda", and accompanied with + * the error that was seen on it. + * + * Returns number of disks with errors filled in the @errors array or -1 on + * error. + */ +int +virDomainGetDiskErrors(virDomainPtr dom, + virDomainDiskErrorPtr errors, + int maxerrors, + unsigned int flags) +{ + VIR_DOMAIN_DEBUG(dom, "errors=%p, maxerrors=%d, flags=%x", + errors, maxerrors, flags); + + virResetLastError(); + + if (!VIR_IS_DOMAIN(dom)) { + virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + if (dom->conn->flags & VIR_CONNECT_RO) { + virLibDomainError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + if (dom->conn->driver->domainGetDiskErrors) { + int ret = dom->conn->driver->domainGetDiskErrors(dom, errors, + maxerrors, flags); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(dom->conn); + return -1; +} diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 1c4e0a3..ced9fb3 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -519,6 +519,7 @@ LIBVIRT_0.9.9 { LIBVIRT_0.9.10 { global: virDomainGetCPUStats; + virDomainGetDiskErrors; virDomainPMSuspendForDuration; virDomainShutdownFlags; virStorageVolResize; diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 61b96e9..33b468a 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -1427,6 +1427,39 @@ cleanup: } static int +remoteDeserializeDomainDiskErrors(remote_domain_disk_error *ret_errors_val, + u_int ret_errors_len, + int limit, + virDomainDiskErrorPtr errors, + int maxerrors) +{ + int i = 0; + int j; + + if (ret_errors_len > limit || ret_errors_len > maxerrors) { + remoteError(VIR_ERR_RPC, "%s", + _("returned number of disk errors exceeds limit")); + goto error; + } + + for (i = 0; i < ret_errors_len; i++) { + if (!(errors[i].disk = strdup(ret_errors_val[i].disk))) { + virReportOOMError(); + goto error; + } + errors[i].error = ret_errors_val[i].error; + } + + return 0; + +error: + for (j = 0; j < i; j++) + VIR_FREE(errors[i].disk); + + return -1; +} + +static int remoteDomainBlockStatsFlags(virDomainPtr domain, const char *path, virTypedParameterPtr params, @@ -4849,6 +4882,7 @@ static virDriver remote_driver = { .domainSetNumaParameters = remoteDomainSetNumaParameters, /* 0.9.9 */ .domainGetNumaParameters = remoteDomainGetNumaParameters, /* 0.9.9 */ .domainGetCPUStats = remoteDomainGetCPUStats, /* 0.9.10 */ + .domainGetDiskErrors = remoteDomainGetDiskErrors, /* 0.9.10 */ }; static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index b58925a..547c108 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -219,6 +219,11 @@ const REMOTE_DOMAIN_GET_CPU_STATS_NCPUS_MAX = 128; */ const REMOTE_DOMAIN_GET_CPU_STATS_MAX = 2048; +/* + * Upper limit on number of disks with errors + */ +const REMOTE_DOMAIN_DISK_ERRORS_MAX = 256; + /* UUID. VIR_UUID_BUFLEN definition comes from libvirt.h */ typedef opaque remote_uuid[VIR_UUID_BUFLEN]; @@ -359,6 +364,10 @@ struct remote_node_get_memory_stats { unsigned hyper value; }; +struct remote_domain_disk_error { + remote_nonnull_string disk; + int error; +}; /*----- Calls. -----*/ @@ -2397,6 +2406,16 @@ struct remote_domain_shutdown_flags_args { unsigned int flags; }; +struct remote_domain_get_disk_errors_args { + remote_nonnull_domain dom; + int maxerrors; + unsigned int flags; +}; + +struct remote_domain_get_disk_errors_ret { + remote_domain_disk_error errors<REMOTE_DOMAIN_DISK_ERRORS_MAX>; /* insert@1 */ +}; + /*----- Protocol. -----*/ @@ -2708,7 +2727,8 @@ enum remote_procedure { REMOTE_PROC_STORAGE_VOL_RESIZE = 260, /* autogen autogen */ REMOTE_PROC_DOMAIN_PM_SUSPEND_FOR_DURATION = 261, /* autogen autogen */ - REMOTE_PROC_DOMAIN_GET_CPU_STATS = 262 /* skipgen skipgen */ + REMOTE_PROC_DOMAIN_GET_CPU_STATS = 262, /* skipgen skipgen */ + REMOTE_PROC_DOMAIN_GET_DISK_ERRORS = 263 /* autogen autogen */ /* * Notice how the entries are grouped in sets of 10 ? diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index 5eac9bf..e031d04 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -94,6 +94,10 @@ struct remote_node_get_memory_stats { remote_nonnull_string field; uint64_t value; }; +struct remote_domain_disk_error { + remote_nonnull_string disk; + int error; +}; struct remote_open_args { remote_string name; u_int flags; @@ -1866,6 +1870,17 @@ struct remote_domain_shutdown_flags_args { remote_nonnull_domain dom; u_int flags; }; +struct remote_domain_get_disk_errors_args { + remote_nonnull_domain dom; + int maxerrors; + u_int flags; +}; +struct remote_domain_get_disk_errors_ret { + struct { + u_int errors_len; + remote_domain_disk_error * errors_val; + } errors; +}; enum remote_procedure { REMOTE_PROC_OPEN = 1, REMOTE_PROC_CLOSE = 2, @@ -2129,4 +2144,5 @@ enum remote_procedure { REMOTE_PROC_STORAGE_VOL_RESIZE = 260, REMOTE_PROC_DOMAIN_PM_SUSPEND_FOR_DURATION = 261, REMOTE_PROC_DOMAIN_GET_CPU_STATS = 262, + REMOTE_PROC_DOMAIN_GET_DISK_ERRORS = 263, }; diff --git a/src/rpc/gendispatch.pl b/src/rpc/gendispatch.pl index 446f229..72c1159 100755 --- a/src/rpc/gendispatch.pl +++ b/src/rpc/gendispatch.pl @@ -539,6 +539,39 @@ elsif ($opt_b) { } elsif ($ret_member =~ m/^remote_nonnull_string (\S+)<\S+>;/) { # error out on unannotated arrays die "remote_nonnull_string array without insert@<offset> annotation: $ret_member"; + } elsif ($ret_member =~ m/^remote_domain_disk_error (\S+)<(\S+)>;\s*\/\*\s*insert@(\d+)\s*\*\//) { + push(@vars_list, + "virDomainDiskErrorPtr $1 = NULL", + "int len"); + splice(@args_list, int($3), 0, ("$1")); + push(@getters_list, + " if (args->max$1 > $2) {", + " virNetError(VIR_ERR_INTERNAL_ERROR, \"%s\",", + " _(\"max$1 too large\"));", + " goto cleanup;", + " }", + "", + " if (VIR_ALLOC_N($1, args->max$1) < 0) {", + " virReportOOMError();", + " goto cleanup;", + " }\n"); + $single_ret_var = "len"; + $single_ret_check = " < 0"; + push(@ret_list, + "if (remoteSerializeDomainDiskErrors($1, len,", + " &ret->$1.$1_val,", + " &ret->$1.$1_len) < 0)", + " goto cleanup;\n"); + push(@free_list, + " if ($1) {", + " int i;", + " for (i = 0; i < len; i++)", + " VIR_FREE(${1}[i].disk);", + " }", + " VIR_FREE($1);"); + } elsif ($ret_member =~ m/^remote_domain_disk_error (\S+)<\S+>;/) { + # error out on unannotated arrays + die "remote_domain_disk_error array without insert@<offset> annotation: $ret_member"; } elsif ($ret_member =~ m/^remote_nonnull_string (\S+);/) { if ($call->{ProcName} eq "GetType") { # SPECIAL: virConnectGetType returns a constant string that must @@ -1297,6 +1330,20 @@ elsif ($opt_k) { } elsif ($ret_member =~ m/^remote_typed_param (\S+)<\S+>;/) { # error out on unannotated arrays die "remote_typed_param array without insert@<offset> annotation: $ret_member"; + } elsif ($ret_member =~ m/^remote_domain_disk_error (\S+)<(\S+)>;\s*\/\*\s*insert@(\d+)\s*\*\//) { + splice(@args_list, int($3), 0, ("virDomainDiskErrorPtr $1")); + push(@ret_list, "rv = ret.$1.$1_len;"); + push(@ret_list2, + "if (remoteDeserializeDomainDiskErrors(ret.$1.$1_val,", + " ret.$1.$1_len,", + " $2,", + " $1,", + " max$1) < 0)", + " goto cleanup;"); + $single_ret_cleanup = 1; + } elsif ($ret_member =~ m/^remote_domain_disk_error (\S+)<\S+>;/) { + # error out on unannotated arrays + die "remote_domain_disk_error array without insert@<offset> annotation: $ret_member"; } elsif ($ret_member =~ m/^int (\S+);/) { my $arg_name = $1; -- 1.7.8.4 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list