// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2009 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
//
// BOINC is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with BOINC. If not, see .
// client-specific GPU code. Mostly GPU detection
#define USE_CHILD_PROCESS_TO_DETECT_GPUS 1
#include "cpp.h"
#ifdef _WIN32
#include "boinc_win.h"
#ifdef _MSC_VER
#define snprintf _snprintf
#define chdir _chdir
#endif
#else
#include "config.h"
#include
#include
#endif
#include "coproc.h"
#include "file_names.h"
#include "util.h"
#include "str_replace.h"
#include "client_msgs.h"
#include "client_state.h"
using std::string;
using std::vector;
#ifndef _WIN32
jmp_buf resume;
void segv_handler(int) {
longjmp(resume, 1);
}
#endif
vector ati_gpus;
vector nvidia_gpus;
vector intel_gpus;
vector ati_opencls;
vector nvidia_opencls;
vector intel_gpu_opencls;
vector cpu_opencls;
static char* client_path;
static char client_dir[MAXPATHLEN];
void COPROCS::get(
bool use_all, vector&descs, vector&warnings,
IGNORE_GPU_INSTANCE& ignore_gpu_instance
) {
#if USE_CHILD_PROCESS_TO_DETECT_GPUS
int retval = 0;
char buf[256];
retval = launch_child_process_to_detect_gpus();
if (retval) {
snprintf(buf, sizeof(buf),
"launch_child_process_to_detect_gpus() returned error %d",
retval
);
warnings.push_back(buf);
}
retval = read_coproc_info_file(warnings);
if (retval) {
snprintf(buf, sizeof(buf),
"read_coproc_info_file() returned error %d",
retval
);
warnings.push_back(buf);
}
#else
detect_gpus(warnings);
#endif
correlate_gpus(use_all, descs, ignore_gpu_instance);
}
void COPROCS::detect_gpus(std::vector &warnings) {
#ifdef _WIN32
try {
nvidia.get(warnings);
}
catch (...) {
warnings.push_back("Caught SIGSEGV in NVIDIA GPU detection");
}
try {
ati.get(warnings);
}
catch (...) {
warnings.push_back("Caught SIGSEGV in ATI GPU detection");
}
try {
intel_gpu.get(warnings);
}
catch (...) {
warnings.push_back("Caught SIGSEGV in INTEL GPU detection");
}
try {
// OpenCL detection must come last
get_opencl(warnings);
}
catch (...) {
warnings.push_back("Caught SIGSEGV in OpenCL detection");
}
#else
void (*old_sig)(int) = signal(SIGSEGV, segv_handler);
if (setjmp(resume)) {
warnings.push_back("Caught SIGSEGV in NVIDIA GPU detection");
} else {
nvidia.get(warnings);
}
#ifndef __APPLE__ // ATI does not yet support CAL on Macs
if (setjmp(resume)) {
warnings.push_back("Caught SIGSEGV in ATI GPU detection");
} else {
ati.get(warnings);
}
#endif
if (setjmp(resume)) {
warnings.push_back("Caught SIGSEGV in INTEL GPU detection");
} else {
intel_gpu.get(warnings);
}
if (setjmp(resume)) {
warnings.push_back("Caught SIGSEGV in OpenCL detection");
} else {
// OpenCL detection must come last
get_opencl(warnings);
}
signal(SIGSEGV, old_sig);
#endif
}
void COPROCS::correlate_gpus(
bool use_all,
std::vector &descs,
IGNORE_GPU_INSTANCE &ignore_gpu_instance
) {
unsigned int i;
char buf[256], buf2[256];
nvidia.correlate(use_all, ignore_gpu_instance[PROC_TYPE_NVIDIA_GPU]);
ati.correlate(use_all, ignore_gpu_instance[PROC_TYPE_AMD_GPU]);
intel_gpu.correlate(use_all, ignore_gpu_instance[PROC_TYPE_AMD_GPU]);
correlate_opencl(use_all, ignore_gpu_instance);
// NOTE: OpenCL can report a max of only 4GB.
for (i=0; i &warnings) {
MIOFILE mf;
unsigned int i, temp;
FILE* f;
f = boinc_fopen(COPROC_INFO_FILENAME, "wb");
if (!f) return ERR_FOPEN;
mf.init_file(f);
mf.printf(" \n");
for (i=0; i%s\n", warnings[i].c_str());
}
mf.printf(" \n");
fclose(f);
return 0;
}
int COPROCS::read_coproc_info_file(vector &warnings) {
MIOFILE mf;
int retval;
FILE* f;
string s;
COPROC_ATI my_ati_gpu;
COPROC_NVIDIA my_nvidia_gpu;
COPROC_INTEL my_intel_gpu;
OPENCL_DEVICE_PROP ati_opencl;
OPENCL_DEVICE_PROP nvidia_opencl;
OPENCL_DEVICE_PROP intel_gpu_opencl;
OPENCL_CPU_PROP cpu_opencl;
ati_gpus.clear();
nvidia_gpus.clear();
intel_gpus.clear();
ati_opencls.clear();
nvidia_opencls.clear();
intel_gpu_opencls.clear();
cpu_opencls.clear();
f = boinc_fopen(COPROC_INFO_FILENAME, "r");
if (!f) return ERR_FOPEN;
XML_PARSER xp(&mf);
mf.init_file(f);
if (!xp.parse_start("coprocs")) {
fclose(f);
return ERR_XML_PARSE;
}
while (!xp.get_tag()) {
if (xp.match_tag("/coprocs")) {
fclose(f);
return 0;
}
if (xp.match_tag("coproc_ati")) {
retval = my_ati_gpu.parse(xp);
if (retval) {
my_ati_gpu.clear();
} else {
my_ati_gpu.device_num = ati_gpus.size();
ati_gpus.push_back(my_ati_gpu);
}
continue;
}
if (xp.match_tag("coproc_cuda")) {
retval = my_nvidia_gpu.parse(xp);
if (retval) {
my_nvidia_gpu.clear();
} else {
my_nvidia_gpu.device_num = nvidia_gpus.size();
my_nvidia_gpu.pci_info = my_nvidia_gpu.pci_infos[0];
memset(&my_nvidia_gpu.pci_infos[0], 0, sizeof(struct PCI_INFO));
nvidia_gpus.push_back(my_nvidia_gpu);
}
continue;
}
if (xp.match_tag("coproc_intel_gpu")) {
retval = my_intel_gpu.parse(xp);
if (retval) {
my_intel_gpu.clear();
} else {
my_intel_gpu.device_num = intel_gpus.size();
intel_gpus.push_back(my_intel_gpu);
}
continue;
}
if (xp.match_tag("ati_opencl")) {
memset(&ati_opencl, 0, sizeof(ati_opencl));
retval = ati_opencl.parse(xp, "/ati_opencl");
if (retval) {
memset(&ati_opencl, 0, sizeof(ati_opencl));
} else {
ati_opencl.is_used = COPROC_IGNORED;
ati_opencls.push_back(ati_opencl);
}
continue;
}
if (xp.match_tag("nvidia_opencl")) {
memset(&nvidia_opencl, 0, sizeof(nvidia_opencl));
retval = nvidia_opencl.parse(xp, "/nvidia_opencl");
if (retval) {
memset(&nvidia_opencl, 0, sizeof(nvidia_opencl));
} else {
nvidia_opencl.is_used = COPROC_IGNORED;
nvidia_opencls.push_back(nvidia_opencl);
}
continue;
}
if (xp.match_tag("intel_gpu_opencl")) {
memset(&intel_gpu_opencl, 0, sizeof(intel_gpu_opencl));
retval = intel_gpu_opencl.parse(xp, "/intel_gpu_opencl");
if (retval) {
memset(&intel_gpu_opencl, 0, sizeof(intel_gpu_opencl));
} else {
intel_gpu_opencl.is_used = COPROC_IGNORED;
intel_gpu_opencls.push_back(intel_gpu_opencl);
}
continue;
}
if (xp.match_tag("opencl_cpu_prop")) {
memset(&cpu_opencl, 0, sizeof(cpu_opencl));
retval = cpu_opencl.parse(xp);
if (retval) {
memset(&cpu_opencl, 0, sizeof(cpu_opencl));
} else {
cpu_opencl.opencl_prop.is_used = COPROC_IGNORED;
cpu_opencls.push_back(cpu_opencl);
}
continue;
}
if (xp.parse_string("warning", s)) {
warnings.push_back(s);
continue;
}
// TODO: parse OpenCL info for CPU when implemented:
// gstate.host_info.have_cpu_opencl
// gstate.host_info.cpu_opencl_prop
}
fclose(f);
return ERR_XML_PARSE;
}
int COPROCS::launch_child_process_to_detect_gpus() {
#ifdef _WIN32
HANDLE prog;
#else
int prog;
#endif
char quotedDataDir[MAXPATHLEN+2];
char dataDir[MAXPATHLEN];
int retval = 0;
retval = boinc_delete_file(COPROC_INFO_FILENAME);
if (retval) {
msg_printf(0, MSG_INFO,
"Failed to delete old %s. error code %d",
COPROC_INFO_FILENAME, retval
);
} else {
for (;;) {
if (!boinc_file_exists(COPROC_INFO_FILENAME)) break;
boinc_sleep(0.01);
}
}
boinc_getcwd(dataDir);
#ifdef _WIN32
strlcpy(quotedDataDir, "\"", sizeof(quotedDataDir));
strlcat(quotedDataDir, dataDir, sizeof(quotedDataDir));
strlcat(quotedDataDir, "\"", sizeof(quotedDataDir));
#else
strlcpy(quotedDataDir, dataDir, sizeof(quotedDataDir));
#endif
if (log_flags.coproc_debug) {
msg_printf(0, MSG_INFO,
"[coproc] launching child process at %s",
client_path
);
msg_printf(0, MSG_INFO,
"[coproc] relative to directory %s",
client_dir
);
msg_printf(0, MSG_INFO,
"[coproc] with data directory %s",
quotedDataDir
);
}
int argc = 4;
char* const argv[5] = {
const_cast("boinc"),
const_cast("--detect_gpus"),
const_cast("--dir"),
const_cast(quotedDataDir),
NULL
};
chdir(client_dir);
retval = run_program(
client_dir,
client_path,
argc,
argv,
0,
prog
);
chdir(dataDir);
if (retval) {
if (log_flags.coproc_debug) {
msg_printf(0, MSG_INFO,
"[coproc] run_program of child process returned error %d",
retval
);
}
return retval;
}
retval = get_exit_status(prog);
if (retval) {
msg_printf(0, MSG_INFO,
"GPU detection failed. error code %d",
retval
);
}
return 0;
}