We don't need all the platforms gnulib deals with, so this is a cut down version of GNULIB's physmem.c code. This also allows us to integrate libvirt's error reporting functions closer to the error cause. Signed-off-by: Daniel P. Berrangé <berrange@xxxxxxxxxx> --- build-aux/syntax-check.mk | 2 +- src/conf/capabilities.c | 1 - src/util/virhostcpu.c | 1 - src/util/virhostmem.c | 182 +++++++++++++++++++++++++++++++------- 4 files changed, 153 insertions(+), 33 deletions(-) diff --git a/build-aux/syntax-check.mk b/build-aux/syntax-check.mk index 687a4ef368..359688ea54 100644 --- a/build-aux/syntax-check.mk +++ b/build-aux/syntax-check.mk @@ -2320,7 +2320,7 @@ exclude_file_name_regexp--sc_prohibit_virXXXFree = \ ^(docs/|tests/|examples/|tools/|build-aux/syntax-check\.mk|src/test/test_driver.c|src/libvirt_public.syms|include/libvirt/libvirt-(domain|network|nodedev|storage|stream|secret|nwfilter|interface|domain-snapshot).h|src/libvirt-(domain|qemu|network|nodedev|storage|stream|secret|nwfilter|interface|domain-snapshot).c$$) exclude_file_name_regexp--sc_prohibit_sysconf_pagesize = \ - ^(build-aux/syntax-check\.mk|src/util/virutil\.c)$$ + ^(build-aux/syntax-check\.mk|src/util/vir(hostmem|util)\.c)$$ exclude_file_name_regexp--sc_prohibit_pthread_create = \ ^(build-aux/syntax-check\.mk|src/util/virthread\.c|tests/.*)$$ diff --git a/src/conf/capabilities.c b/src/conf/capabilities.c index 9a39858280..ade04b7cd6 100644 --- a/src/conf/capabilities.c +++ b/src/conf/capabilities.c @@ -26,7 +26,6 @@ #include "capabilities.h" #include "cpu_conf.h" #include "domain_conf.h" -#include "physmem.h" #include "storage_conf.h" #include "viralloc.h" #include "virarch.h" diff --git a/src/util/virhostcpu.c b/src/util/virhostcpu.c index f3adc1b4ae..a26e339a0a 100644 --- a/src/util/virhostcpu.c +++ b/src/util/virhostcpu.c @@ -41,7 +41,6 @@ #include "viralloc.h" #define LIBVIRT_VIRHOSTCPUPRIV_H_ALLOW #include "virhostcpupriv.h" -#include "physmem.h" #include "virerror.h" #include "virarch.h" #include "virfile.h" diff --git a/src/util/virhostmem.c b/src/util/virhostmem.c index 34e6f7adcf..9c08b9bd78 100644 --- a/src/util/virhostmem.c +++ b/src/util/virhostmem.c @@ -33,9 +33,13 @@ # include <sys/resource.h> #endif +#ifdef WIN32 +# define WIN32_LEAN_AND_MEAN +# include <windows.h> +#endif + #include "viralloc.h" #include "virhostmem.h" -#include "physmem.h" #include "virerror.h" #include "virarch.h" #include "virfile.h" @@ -577,13 +581,151 @@ virHostMemGetParameters(virTypedParameterPtr params G_GNUC_UNUSED, } +#ifdef WIN32 +/* MEMORYSTATUSEX is missing from older windows headers, so define + a local replacement. */ +typedef struct +{ + DWORD dwLength; + DWORD dwMemoryLoad; + DWORDLONG ullTotalPhys; + DWORDLONG ullAvailPhys; + DWORDLONG ullTotalPageFile; + DWORDLONG ullAvailPageFile; + DWORDLONG ullTotalVirtual; + DWORDLONG ullAvailVirtual; + DWORDLONG ullAvailExtendedVirtual; +} lMEMORYSTATUSEX; +typedef WINBOOL(WINAPI *PFN_MS_EX) (lMEMORYSTATUSEX*); +#endif /* !WIN32 */ + +static unsigned long long +virHostMemGetTotal(void) +{ +#if defined HAVE_SYSCTLBYNAME + /* This works on freebsd & macOS. */ + unsigned long long physmem = 0; + size_t len = sizeof(physmem); + + if (sysctlbyname("hw.physmem", &physmem, &len, NULL, 0) < 0) { + virReportSystemError(errno, "%s", + _("Unable to query memory total")); + return 0; + } + + return physmem; +#elif defined _SC_PHYS_PAGES && defined _SC_PAGESIZE + /* this works on linux */ + long long pages; + long long pagesize; + if ((pages = sysconf(_SC_PHYS_PAGES)) < 0) { + virReportSystemError(errno, "%s", + _("Unable to query memory total")); + return 0; + } + if ((pagesize = sysconf(_SC_PAGESIZE)) < 0) { + virReportSystemError(errno, "%s", + _("Unable to query memory page size")); + return 0; + } + return (unsigned long long)pages * (unsigned long long)pagesize; +#elif defined WIN32 + PFN_MS_EX pfnex; + HMODULE h = GetModuleHandle("kernel32.dll"); + + if (!h) { + virReportSystemError(errno, "%s", + _("Unable to access kernel32.dll")); + return 0; + } + + /* Use GlobalMemoryStatusEx if available. */ + if ((pfnex = (PFN_MS_EX) GetProcAddress(h, "GlobalMemoryStatusEx"))) { + lMEMORYSTATUSEX lms_ex; + lms_ex.dwLength = sizeof(lms_ex); + if (!pfnex(&lms_ex)) { + virReportSystemError(EIO, "%s", + _("Unable to query memory total")); + return 0; + } + return lms_ex.ullTotalPhys; + } else { + /* Fall back to GlobalMemoryStatus which is always available. + but returns wrong results for physical memory > 4GB. */ + MEMORYSTATUS ms; + GlobalMemoryStatus(&ms); + return ms.dwTotalPhys; + } +#endif +} + + +static unsigned long long +virHostMemGetAvailable(void) +{ +#if defined HAVE_SYSCTLBYNAME + /* This works on freebsd and macOS */ + unsigned long long usermem = 0; + size_t len = sizeof(usermem); + + if (sysctlbyname("hw.usermem", &usermem, &len, NULL, 0) < 0) { + virReportSystemError(errno, "%s", + _("Unable to query memory available")); + return 0; + } + + return usermem; +#elif defined _SC_AVPHYS_PAGES && defined _SC_PAGESIZE + /* this works on linux */ + long long pages; + long long pagesize; + if ((pages = sysconf(_SC_AVPHYS_PAGES)) < 0) { + virReportSystemError(errno, "%s", + _("Unable to query memory available")); + return 0; + } + if ((pagesize = sysconf(_SC_PAGESIZE)) < 0) { + virReportSystemError(errno, "%s", + _("Unable to query memory page size")); + return 0; + } + return (unsigned long long)pages * (unsigned long long)pagesize; +#elif defined WIN32 + PFN_MS_EX pfnex; + HMODULE h = GetModuleHandle("kernel32.dll"); + + if (!h) { + virReportSystemError(errno, "%s", + _("Unable to access kernel32.dll")); + return 0; + } + + /* Use GlobalMemoryStatusEx if available. */ + if ((pfnex = (PFN_MS_EX) GetProcAddress(h, "GlobalMemoryStatusEx"))) { + lMEMORYSTATUSEX lms_ex; + lms_ex.dwLength = sizeof(lms_ex); + if (!pfnex(&lms_ex)) { + virReportSystemError(EIO, "%s", + _("Unable to query memory available")); + return 0; + } + return lms_ex.ullAvailPhys; + } else { + /* Fall back to GlobalMemoryStatus which is always available. + but returns wrong results for physical memory > 4GB */ + MEMORYSTATUS ms; + GlobalMemoryStatus(&ms); + return ms.dwAvailPhys; + } +#endif +} + + static int virHostMemGetCellsFreeFake(unsigned long long *freeMems, int startCell, int maxCells G_GNUC_UNUSED) { - double avail = physmem_available(); - if (startCell != 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("start cell %d out of range (0-%d)"), @@ -591,13 +733,8 @@ virHostMemGetCellsFreeFake(unsigned long long *freeMems, return -1; } - freeMems[0] = (unsigned long long)avail; - - if (!freeMems[0]) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Cannot determine free memory")); + if ((freeMems[0] = virHostMemGetAvailable()) == 0) return -1; - } return 1; } @@ -606,28 +743,13 @@ static int virHostMemGetInfoFake(unsigned long long *mem, unsigned long long *freeMem) { - if (mem) { - double total = physmem_total(); - if (!total) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Cannot determine free memory")); - return -1; - } - - *mem = (unsigned long long) total; - } - - if (freeMem) { - double avail = physmem_available(); - - if (!avail) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Cannot determine free memory")); - return -1; - } + if (mem && + (*mem = virHostMemGetTotal()) == 0) + return -1; - *freeMem = (unsigned long long) avail; - } + if (freeMem && + (*freeMem = virHostMemGetAvailable()) == 0) + return -1; return 0; } -- 2.24.1