// This file is part of BOINC. // http://boinc.berkeley.edu // Copyright (C) 2008 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 . #ifndef _USING_FCGI_ #include "boinc_fcgi.h" #else #include #endif #include #include #ifdef _WIN32 #ifndef SIM #include #endif #else #ifdef __APPLE__ // Suppress obsolete warning when building for OS 10.3.9 #define DLOPEN_NO_WARN #endif #include #endif #include "error_numbers.h" #include "filesys.h" #include "parse.h" #include "str_util.h" #include "coproc.h" using std::string; using std::vector; #ifndef _USING_FCGI_ using std::perror; #endif #ifndef _USING_FCGI_ void COPROC::write_xml(MIOFILE& f) { f.printf( "\n" " %s\n" " %d\n" "\n", type, count ); } #endif int COPROC_REQ::parse(MIOFILE& fin) { char buf[1024]; strcpy(type, ""); count = 0; while (fin.fgets(buf, sizeof(buf))) { if (match_tag(buf, "")) { if (!strlen(type)) return ERR_XML_PARSE; return 0; } if (parse_str(buf, "", type, sizeof(type))) continue; if (parse_double(buf, "", count)) continue; } return ERR_XML_PARSE; } int COPROC::parse(MIOFILE& fin) { char buf[1024]; strcpy(type, ""); count = 0; used = 0; req_secs = 0; estimated_delay = 0; req_instances = 0; while (fin.fgets(buf, sizeof(buf))) { if (match_tag(buf, "")) { if (!strlen(type)) return ERR_XML_PARSE; return 0; } if (parse_str(buf, "", type, sizeof(type))) continue; if (parse_int(buf, "", count)) continue; if (parse_double(buf, "", req_secs)) continue; if (parse_double(buf, "", req_instances)) continue; if (parse_double(buf, "", estimated_delay)) continue; } return ERR_XML_PARSE; } void COPROCS::summary_string(char* buf, int len) { char bigbuf[8192], buf2[1024]; strcpy(bigbuf, ""); for (unsigned int i=0; itype, "CUDA")) { COPROC_CUDA* cp2 = (COPROC_CUDA*) cp; int mem = (int)(cp2->prop.dtotalGlobalMem/MEGA); sprintf(buf2, "[CUDA|%s|%d|%dMB|%d]", cp2->prop.name, cp2->count, mem, cp2->display_driver_version ); strcat(bigbuf, buf2); } else if (!strcmp(cp->type, "CAL")){ COPROC_ATI* cp2 =(COPROC_ATI*) cp; sprintf(buf2,"[CAL|%s|%d|%dMB|%s]", cp2->name, cp2->count, cp2->attribs.localRAM, cp2->version ); strcat(bigbuf,buf2); } } bigbuf[len-1] = 0; strcpy(buf, bigbuf); } void COPROCS::get(bool use_all, vector&descs, vector&warnings) { COPROC_CUDA::get(*this, use_all, descs, warnings); #ifndef __APPLE__ // ATI does not yet support CAL on Macs COPROC_ATI::get(*this, descs, warnings); #endif } // used only to parse scheduler request messages // int COPROCS::parse(FILE* fin) { char buf[1024]; while (fgets(buf, sizeof(buf), fin)) { if (match_tag(buf, "")) { return 0; } if (strstr(buf, "")) { COPROC_CUDA* cc = new COPROC_CUDA; int retval = cc->parse(fin); if (!retval) { coprocs.push_back(cc); } } if (strstr(buf, "")) { COPROC_ATI* cc = new COPROC_ATI; int retval = cc->parse(fin); if (!retval) { coprocs.push_back(cc); } } } return ERR_XML_PARSE; } COPROC* COPROCS::lookup(const char* type) { for (unsigned int i=0; itype)) return cp; } return NULL; } #ifdef _WIN32 #endif // return 1/-1/0 if device 1 is more/less/same capable than device 2. // If "loose", ignore FLOPS and tolerate small memory diff // int cuda_compare(COPROC_CUDA& c1, COPROC_CUDA& c2, bool loose) { if (c1.prop.major > c2.prop.major) return 1; if (c1.prop.major < c2.prop.major) return -1; if (c1.prop.minor > c2.prop.minor) return 1; if (c1.prop.minor < c2.prop.minor) return -1; if (c1.cuda_version > c2.cuda_version) return 1; if (c1.cuda_version < c2.cuda_version) return -1; if (loose) { if (c1.prop.totalGlobalMem > 1.4*c2.prop.totalGlobalMem) return 1; if (c1.prop.totalGlobalMem < .7* c2.prop.totalGlobalMem) return -1; return 0; } if (c1.prop.totalGlobalMem > c2.prop.totalGlobalMem) return 1; if (c1.prop.totalGlobalMem < c2.prop.totalGlobalMem) return -1; double s1 = c1.flops_estimate(); double s2 = c2.flops_estimate(); if (s1 > s2) return 1; if (s1 < s2) return -1; return 0; } #ifdef _WIN32 typedef int (__stdcall *CUDA_GDC)(int *count); typedef int (__stdcall *CUDA_GDP)(struct cudaDeviceProp *prop, int device); typedef int (__stdcall *CUDA_GDV)(int* version); typedef int (__stdcall *CUDA_GDI)(int); typedef int (__stdcall *CUDA_GDG)(int*, int); typedef int (__stdcall *CUDA_GDA)(int*, int, int); typedef int (__stdcall *CUDA_GDN)(char*, int, int); typedef int (__stdcall *CUDA_GDM)(unsigned int*, int); typedef int (__stdcall *CUDA_GDCC)(int*, int*, int); CUDA_GDC __cuDeviceGetCount = NULL; //CUDA_GDP __cuDeviceGetProperties = NULL; CUDA_GDV __cuDriverGetVersion = NULL; CUDA_GDI __cuInit = NULL; CUDA_GDG __cuDeviceGet = NULL; CUDA_GDA __cuDeviceGetAttribute = NULL; CUDA_GDN __cuDeviceGetName = NULL; CUDA_GDM __cuDeviceTotalMem = NULL; CUDA_GDCC __cuDeviceComputeCapability = NULL; #else void* cudalib; int (*__cuInit)(int); int (*__cuDeviceGetCount)(int*); //int (*__cuDeviceGetProperties)(cudaDeviceProp*, int); int (*__cuDriverGetVersion)(int*); int (*__cuDeviceGet)(int*, int); int (*__cuDeviceGetAttribute)(int*, int, int); int (*__cuDeviceGetName)(char*, int, int); int (*__cuDeviceTotalMem)(unsigned int*, int); int (*__cuDeviceComputeCapability)(int*, int*, int); #endif // NVIDIA interfaces are documented here: // http://developer.download.nvidia.com/compute/cuda/2_3/toolkit/docs/online/index.html void COPROC_CUDA::get( COPROCS& coprocs, bool use_all, // if false, use only those equivalent to most capable vector& descs, vector& warnings ) { int count, retval; char buf[256]; #ifdef _WIN32 HMODULE cudalib = LoadLibrary("nvcuda.dll"); if (!cudalib) { warnings.push_back("No NVIDIA library found"); return; } __cuDeviceGetCount = (CUDA_GDC)GetProcAddress(cudalib, "cuDeviceGetCount"); //__cuDeviceGetProperties = (CUDA_GDP)GetProcAddress(cudalib, "cuDeviceGetProperties"); __cuDriverGetVersion = (CUDA_GDV)GetProcAddress(cudalib, "cuDriverGetVersion" ); __cuInit = (CUDA_GDI)GetProcAddress(cudalib, "cuInit" ); __cuDeviceGet = (CUDA_GDG)GetProcAddress(cudalib, "cuDeviceGet" ); __cuDeviceGetAttribute = (CUDA_GDA)GetProcAddress(cudalib, "cuDeviceGetAttribute" ); __cuDeviceGetName = (CUDA_GDN)GetProcAddress(cudalib, "cuDeviceGetName" ); __cuDeviceTotalMem = (CUDA_GDM)GetProcAddress(cudalib, "cuDeviceTotalMem" ); __cuDeviceComputeCapability = (CUDA_GDCC)GetProcAddress(cudalib, "cuDeviceComputeCapability" ); #ifndef SIM NvAPI_Status nvapiStatus; NvDisplayHandle hDisplay; NV_DISPLAY_DRIVER_VERSION Version; memset(&Version, 0, sizeof(Version)); Version.version = NV_DISPLAY_DRIVER_VERSION_VER; NvAPI_Initialize(); for (int i=0; ; i++) { nvapiStatus = NvAPI_EnumNvidiaDisplayHandle(i, &hDisplay); if (nvapiStatus != NVAPI_OK) break; nvapiStatus = NvAPI_GetDisplayDriverVersion(hDisplay, &Version); if (nvapiStatus == NVAPI_OK) break; } #endif #else #ifdef __APPLE__ cudalib = dlopen("/usr/local/cuda/lib/libcuda.dylib", RTLD_NOW); #else cudalib = dlopen("libcuda.so", RTLD_NOW); #endif if (!cudalib) { warnings.push_back("No NVIDIA library found"); return; } __cuDeviceGetCount = (int(*)(int*)) dlsym(cudalib, "cuDeviceGetCount"); //__cuDeviceGetProperties = (int(*)(cudaDeviceProp*, int)) dlsym( cudalib, "cuDeviceGetProperties" ); __cuDriverGetVersion = (int(*)(int*)) dlsym( cudalib, "cuDriverGetVersion" ); __cuInit = (int(*)(int)) dlsym( cudalib, "cuInit" ); __cuDeviceGet = (int(*)(int*, int)) dlsym( cudalib, "cuDeviceGet" ); __cuDeviceGetAttribute = (int(*)(int*, int, int)) dlsym( cudalib, "cuDeviceGetAttribute" ); __cuDeviceGetName = (int(*)(char*, int, int)) dlsym( cudalib, "cuDeviceGetName" ); __cuDeviceTotalMem = (int(*)(unsigned int*, int)) dlsym( cudalib, "cuDeviceTotalMem" ); __cuDeviceComputeCapability = (int(*)(int*, int*, int)) dlsym( cudalib, "cuDeviceComputeCapability" ); #endif if (!__cuDriverGetVersion) { warnings.push_back("cuDriverGetVersion() missing from NVIDIA library"); return; } if (!__cuInit) { warnings.push_back("cuInit() missing from NVIDIA library"); return; } if (!__cuDeviceGetCount) { warnings.push_back("cuDeviceGetCount() missing from NVIDIA library"); return; } if (!__cuDeviceGet) { warnings.push_back("cuDeviceGet() missing from NVIDIA library"); return; } if (!__cuDeviceGetAttribute) { warnings.push_back("cuDeviceGetAttribute() missing from NVIDIA library"); return; } if (!__cuDeviceTotalMem) { warnings.push_back("cuDeviceTotalMem() missing from NVIDIA library"); return; } if (!__cuDeviceComputeCapability) { warnings.push_back("cuDeviceComputeCapability() missing from NVIDIA library"); return; } retval = (*__cuInit)(0); if (retval) { sprintf(buf, "cuInit() returned %d", retval); warnings.push_back(buf); return; } int cuda_version; retval = (*__cuDriverGetVersion)(&cuda_version); if (retval) { sprintf(buf, "cuDriverGetVersion() returned %d", retval); warnings.push_back(buf); return; } vector gpus; retval = (*__cuDeviceGetCount)(&count); if (retval) { sprintf(buf, "cuDeviceGetCount() returned %d", retval); warnings.push_back(buf); return; } int j; unsigned int i; COPROC_CUDA cc; string s; for (j=0; j 100) continue; // e.g. 9999 is an error #if defined(_WIN32) && !defined(SIM) cc.display_driver_version = Version.drvVersion; #else cc.display_driver_version = 0; #endif cc.cuda_version = cuda_version; cc.device_num = j; gpus.push_back(cc); } if (!gpus.size()) { warnings.push_back("No CUDA-capable NVIDIA GPUs found"); return; } // identify the most capable instance // COPROC_CUDA best; for (i=0; i 0) { best = gpus[i]; } } // see which other instances are equivalent, // and set the "count" and "device_nums" fields // best.count = 0; for (i=0; itype, "CUDA"); cc->count = count; for (int i=0; idevice_nums[i] = i; } cc->display_driver_version = 18000; cc->cuda_version = 2020; strcpy(cc->prop.name, "Fake NVIDIA GPU"); cc->prop.totalGlobalMem = 256*1024*1024; cc->prop.sharedMemPerBlock = 100; cc->prop.regsPerBlock = 8; cc->prop.warpSize = 10; cc->prop.memPitch = 10; cc->prop.maxThreadsPerBlock = 20; cc->prop.maxThreadsDim[0] = 2; cc->prop.maxThreadsDim[1] = 2; cc->prop.maxThreadsDim[2] = 2; cc->prop.maxGridSize[0] = 10; cc->prop.maxGridSize[1] = 10; cc->prop.maxGridSize[2] = 10; cc->prop.totalConstMem = 10; cc->prop.major = 1; cc->prop.minor = 2; cc->prop.clockRate = 1250000; cc->prop.textureAlignment = 1000; cc->prop.multiProcessorCount = 14; coprocs.coprocs.push_back(cc); } #ifndef _USING_FCGI_ void COPROC_CUDA::write_xml(MIOFILE& f) { f.printf( "\n" " %d\n" " %s\n" " %f\n" " %f\n" " %f\n" " %d\n" " %d\n" " %u\n" " %u\n" " %d\n" " %d\n" " %u\n" " %d\n" " %d %d %d\n" " %d %d %d\n" " %u\n" " %d\n" " %d\n" " %d\n" " %u\n" " %d\n" " %d\n" "\n", count, prop.name, req_secs, req_instances, estimated_delay, display_driver_version, cuda_version, (unsigned int)prop.totalGlobalMem, (unsigned int)prop.sharedMemPerBlock, prop.regsPerBlock, prop.warpSize, (unsigned int)prop.memPitch, prop.maxThreadsPerBlock, prop.maxThreadsDim[0], prop.maxThreadsDim[1], prop.maxThreadsDim[2], prop.maxGridSize[0], prop.maxGridSize[1], prop.maxGridSize[2], (unsigned int)prop.totalConstMem, prop.major, prop.minor, prop.clockRate, (unsigned int)prop.textureAlignment, prop.deviceOverlap, prop.multiProcessorCount ); } #endif void COPROC_CUDA::clear() { count = 0; used = 0; req_secs = 0; req_instances = 0; estimated_delay = -1; // mark as absent cuda_version = 0; display_driver_version = 0; strcpy(prop.name, ""); prop.totalGlobalMem = 0; prop.sharedMemPerBlock = 0; prop.regsPerBlock = 0; prop.warpSize = 0; prop.memPitch = 0; prop.maxThreadsPerBlock = 0; prop.maxThreadsDim[0] = 0; prop.maxThreadsDim[1] = 0; prop.maxThreadsDim[2] = 0; prop.maxGridSize[0] = 0; prop.maxGridSize[1] = 0; prop.maxGridSize[2] = 0; prop.clockRate = 0; prop.totalConstMem = 0; prop.major = 0; prop.minor = 0; prop.textureAlignment = 0; prop.deviceOverlap = 0; prop.multiProcessorCount = 0; } int COPROC_CUDA::parse(FILE* fin) { char buf[1024], buf2[256]; clear(); while (fgets(buf, sizeof(buf), fin)) { if (strstr(buf, "")) { return 0; } if (parse_int(buf, "", count)) continue; if (parse_double(buf, "", req_secs)) continue; if (parse_double(buf, "", req_instances)) continue; if (parse_double(buf, "", estimated_delay)) continue; if (parse_str(buf, "", prop.name, sizeof(prop.name))) continue; if (parse_int(buf, "", display_driver_version)) continue; if (parse_int(buf, "", cuda_version)) continue; if (parse_double(buf, "", prop.dtotalGlobalMem)) continue; if (parse_int(buf, "", (int&)prop.sharedMemPerBlock)) continue; if (parse_int(buf, "", prop.regsPerBlock)) continue; if (parse_int(buf, "", prop.warpSize)) continue; if (parse_int(buf, "", (int&)prop.memPitch)) continue; if (parse_int(buf, "", prop.maxThreadsPerBlock)) continue; if (parse_str(buf, "", buf2, sizeof(buf2))) { // can't use sscanf here (FCGI) // prop.maxThreadsDim[0] = atoi(buf2); char* p = strchr(buf2, ' '); if (p) { p++; prop.maxThreadsDim[1] = atoi(p); p = strchr(p, ' '); if (p) { p++; prop.maxThreadsDim[2] = atoi(p); } } continue; } if (parse_str(buf, "", buf2, sizeof(buf2))) { prop.maxGridSize[0] = atoi(buf2); char* p = strchr(buf2, ' '); if (p) { p++; prop.maxGridSize[1] = atoi(p); p = strchr(p, ' '); if (p) { p++; prop.maxGridSize[2] = atoi(p); } } continue; } if (parse_int(buf, "", prop.clockRate)) continue; if (parse_int(buf, "", (int&)prop.totalConstMem)) continue; if (parse_int(buf, "", prop.major)) continue; if (parse_int(buf, "", prop.minor)) continue; if (parse_int(buf, "", (int&)prop.textureAlignment)) continue; if (parse_int(buf, "", prop.deviceOverlap)) continue; if (parse_int(buf, "", prop.multiProcessorCount)) continue; } return ERR_XML_PARSE; } // check whether each GPU is running a graphics app (assume yes) // return true if there's been a change since last time // bool COPROC_CUDA::check_running_graphics_app() { int retval, j; bool change = false; for (j=0; j& descs, vector& warnings ) { CALuint numDevices, cal_major, cal_minor, cal_imp; CALdevice device; CALdeviceinfo info; CALdeviceattribs attribs; char buf[256]; int retval; attribs.struct_size = sizeof(CALdeviceattribs); device = 0; numDevices =0; #ifdef _WIN32 #if defined _M_X64 HINSTANCE callib = LoadLibrary("aticalrt64.dll"); #else HINSTANCE callib = LoadLibrary("aticalrt.dll"); #endif if (!callib) { warnings.push_back("No ATI library found."); return; } __calInit = (ATI_GDI)GetProcAddress(callib, "calInit" ); __calDeviceGetCount = (ATI_GDC)GetProcAddress(callib, "calDeviceGetCount" ); __calGetVersion = (ATI_VER)GetProcAddress(callib, "calGetVersion" ); __calDeviceGetInfo = (ATI_INFO)GetProcAddress(callib, "calDeviceGetInfo" ); __calDeviceGetAttribs =(ATI_ATTRIBS)GetProcAddress(callib, "calDeviceGetAttribs" ); __calShutdown = (ATI_CLOSE)GetProcAddress(callib, "calShutdown" ); #else void* callib; callib = dlopen("libaticalrt.so", RTLD_NOW); if (!callib) { warnings.push_back("No ATI library found"); return; } __calInit = (int(*)()) dlsym(callib, "calInit"); __calGetVersion = (int(*)(CALuint*, CALuint*, CALuint*)) dlsym(callib, "calGetVersion"); __calDeviceGetCount = (int(*)(CALuint*)) dlsym(callib, "calDeviceGetCount"); __calDeviceGetAttribs = (int(*)(CALdeviceattribs*, CALuint)) dlsym(callib, "calDeviceGetAttribs"); __calDeviceGetInfo = (int(*)(CALdeviceinfo*, CALuint)) dlsym(callib, "calDeviceGetInfo"); __calShutdown = (int(*)()) dlsym(callib, "calShutdown"); #endif if (!__calInit) { warnings.push_back("calInit() missing from CAL library"); return; } if (!__calDeviceGetCount) { warnings.push_back("calDeviceGetCount() missing from CAL library"); return; } if (!__calGetVersion) { warnings.push_back("calGetVersion() missing from CAL library"); return; } if (!__calDeviceGetInfo) { warnings.push_back("calDeviceGetInfo() missing from CAL library"); return; } if (!__calDeviceGetAttribs) { warnings.push_back("calDeviceGetAttribs() missing from CAL library"); return; } retval = (*__calInit)(); if (retval != CAL_RESULT_OK) { sprintf(buf, "calInit() returned %d", retval); warnings.push_back(buf); return; } retval = (*__calDeviceGetCount)(&numDevices); if (retval != CAL_RESULT_OK) { sprintf(buf, "calDeviceGetCount() returned %d", retval); warnings.push_back(buf); return; } retval = (*__calGetVersion)(&cal_major, &cal_minor, &cal_imp); if (retval != CAL_RESULT_OK) { sprintf(buf, "calGetVersion() returned %d", retval); warnings.push_back(buf); return; } if (!numDevices) { warnings.push_back("No usable CAL devices found"); return; } COPROC_ATI cc, cc2; string s, gpu_name; vector gpus; for (CALuint i=0; i best.flops_estimate()) { best = gpus[i]; } gpus[i].description(buf); sprintf(buf2, "ATI GPU %d: %s", gpus[i].device_num, buf); descs.push_back(buf2); } for (unsigned int i=0; itype, "ATI"); ccp->count = numDevices; coprocs.coprocs.push_back(ccp); __calShutdown(); } #ifndef _USING_FCGI_ void COPROC_ATI::write_xml(MIOFILE& f) { f.printf( "\n" " %d\n" " %s\n" " %f\n" " %f\n" " %f\n" " %d\n" " %d\n" " %d\n" " %u\n" " %d\n" " %d\n" " %d\n" " %d\n" " %d\n" " %d\n" " %s\n" "\n", count, name, req_secs, req_instances, estimated_delay, attribs.localRAM, attribs.uncachedRemoteRAM, attribs.cachedRemoteRAM, attribs.engineClock, attribs.memoryClock, attribs.wavefrontSize, attribs.numberOfSIMD, attribs.doublePrecision, attribs.pitch_alignment, attribs.surface_alignment, version ); }; #endif void COPROC_ATI::clear() { count = 0; used = 0; req_secs = 0; req_instances = 0; estimated_delay = -1; strcpy(name, ""); strcpy(version, ""); attribs.localRAM = 0; attribs.uncachedRemoteRAM = 0; attribs.cachedRemoteRAM = 0; attribs.engineClock = 0; attribs.memoryClock = 0; attribs.wavefrontSize = 0; attribs.numberOfSIMD = 0; attribs.doublePrecision = CAL_FALSE; attribs.pitch_alignment = 0; attribs.surface_alignment = 0; } int COPROC_ATI::parse(FILE* fin) { char buf[1024]; int n; clear(); while (fgets(buf, sizeof(buf), fin)) { if (strstr(buf, "")) return 0; if (parse_int(buf, "", count)) continue; if (parse_str(buf, "", name, sizeof(name))) continue; if (parse_double(buf, "", req_secs)) continue; if (parse_double(buf, "", req_instances)) continue; if (parse_double(buf, "", estimated_delay)) continue; if (parse_int(buf, "", n)) { attribs.localRAM = n; continue; } if (parse_int(buf, "", n)) { attribs.uncachedRemoteRAM = n; continue; } if (parse_int(buf, "", n)) { attribs.cachedRemoteRAM = n; continue; } if (parse_int(buf, "", n)) { attribs.engineClock = n; continue; } if (parse_int(buf, "", n)) { attribs.memoryClock = n; continue; } if (parse_int(buf, "", n)) { attribs.wavefrontSize = n; continue; } if (parse_int(buf, "" , n)) { attribs.numberOfSIMD = n; continue; } if (parse_int(buf, "", n)) { attribs.doublePrecision = n?CAL_TRUE:CAL_FALSE; continue; } if (parse_int(buf, "", n)) { attribs.pitch_alignment = n; continue; } if (parse_int(buf, "", n)) { attribs.surface_alignment = n; continue; } if (parse_str(buf, "", version, sizeof(version))) continue; } return ERR_XML_PARSE; } void COPROC_ATI::description(char* buf) { sprintf(buf, "%s (CAL version %s, %.0fMB, %.0fGFLOPS)", name, version, attribs.localRAM/1024.*1024., flops_estimate()/1.e9 ); } void fake_ati(COPROCS& coprocs, int count) { COPROC_ATI* cc = new COPROC_ATI; strcpy(cc->type, "ATI"); strcpy(cc->version, "1.2.3"); cc->count = count; cc->attribs.numberOfSIMD = 32; cc->attribs.wavefrontSize = 32; cc->attribs.engineClock = 50; for (int i=0; idevice_nums[i] = i; } coprocs.coprocs.push_back(cc); }