1 // This file is part of BOINC.
2 // http://boinc.berkeley.edu
3 // Copyright (C) 2009 University of California
4 //
5 // BOINC is free software; you can redistribute it and/or modify it
6 // under the terms of the GNU Lesser General Public License
7 // as published by the Free Software Foundation,
8 // either version 3 of the License, or (at your option) any later version.
9 //
10 // BOINC is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 // See the GNU Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public License
16 // along with BOINC. If not, see <http://www.gnu.org/licenses/>.
17
18
19 // client-specific GPU code. Mostly GPU detection
20 //
21 // theory of operation:
22 // there are two ways of detecting GPUs:
23 // - vendor-specific libraries like CUDA and CAL,
24 // which detect only that vendor's GPUs
25 // - OpenCL, which can detect multiple types of GPUs,
26 // including nvidia/amd/intel as well was new types
27 // such as ARM integrated GPUs
28 //
29 // These libraries sometimes crash,
30 // and we've been unable to trap these via signal and exception handlers.
31 // So we do GPU detection in a separate process (boinc --detect_gpus)
32 // This process writes an XML file "coproc_info.xml" containing
33 // - lists of GPU detected via CUDA and CAL
34 // - lists of nvidia/amd/intel GPUs detected via OpenCL
35 // - a list of other GPUs detected via OpenCL
36 //
37 // When the process finishes, the client parses the info file.
38 // Then for each vendor it "correlates" the GPUs, which includes:
39 // - matching up the OpenCL and vendor-specific descriptions, if both exist
40 // - finding the most capable GPU, and seeing which other GPUs
41 // are similar to it in hardware and RAM.
42 // Other GPUs are not used.
43 // - copy these to the COPROCS structure
44 //
45 // Also, some dual-GPU laptops (e.g., Macbook Pro) don't power
46 // down the more powerful GPU until all applications which used
47 // them exit. Doing GPU detection in a second, short-lived process
48 // saves battery life on these laptops.
49 //
50 // GPUs can also be explicitly described in cc_config.xml
51
52
53 #ifndef _DEBUG
54 #define USE_CHILD_PROCESS_TO_DETECT_GPUS 1
55 #endif
56
57 #include "cpp.h"
58
59 #ifdef _WIN32
60 #include "boinc_win.h"
61 #ifdef _MSC_VER
62 #define snprintf _snprintf
63 #define chdir _chdir
64 #endif
65 #else
66 #include "config.h"
67 #include <setjmp.h>
68 #include <signal.h>
69 #endif
70
71 #include "coproc.h"
72 #include "gpu_detect.h"
73 #include "file_names.h"
74 #include "util.h"
75 #include "str_replace.h"
76 #include "client_msgs.h"
77 #include "client_state.h"
78
79 using std::string;
80 using std::vector;
81
82 #ifndef _WIN32
83 jmp_buf resume;
84
segv_handler(int)85 void segv_handler(int) {
86 longjmp(resume, 1);
87 }
88 #endif
89
90 vector<COPROC_ATI> ati_gpus;
91 vector<COPROC_NVIDIA> nvidia_gpus;
92 vector<COPROC_INTEL> intel_gpus;
93 vector<OPENCL_DEVICE_PROP> ati_opencls;
94 vector<OPENCL_DEVICE_PROP> nvidia_opencls;
95 vector<OPENCL_DEVICE_PROP> intel_gpu_opencls;
96 vector<OPENCL_DEVICE_PROP> other_opencls;
97 vector<OPENCL_CPU_PROP> cpu_opencls;
98
99 static char* client_path;
100 // argv[0] from the command used to run client.
101 // May be absolute or relative.
102 static char client_dir[MAXPATHLEN];
103 // current directory at start of client
104
get(bool use_all,vector<string> & descs,vector<string> & warnings,IGNORE_GPU_INSTANCE & ignore_gpu_instance)105 void COPROCS::get(
106 bool use_all, vector<string>&descs, vector<string>&warnings,
107 IGNORE_GPU_INSTANCE& ignore_gpu_instance
108 ) {
109 #if USE_CHILD_PROCESS_TO_DETECT_GPUS
110 int retval = 0;
111 char buf[256];
112
113 retval = launch_child_process_to_detect_gpus();
114 if (retval) {
115 snprintf(buf, sizeof(buf),
116 "launch_child_process_to_detect_gpus() returned error %d",
117 retval
118 );
119 warnings.push_back(buf);
120 }
121 retval = read_coproc_info_file(warnings);
122 if (retval) {
123 snprintf(buf, sizeof(buf),
124 "read_coproc_info_file() returned error %d",
125 retval
126 );
127 warnings.push_back(buf);
128 }
129 #else
130 detect_gpus(warnings);
131 #endif
132 correlate_gpus(use_all, descs, ignore_gpu_instance);
133 }
134
135
detect_gpus(vector<string> & warnings)136 void COPROCS::detect_gpus(vector<string> &warnings) {
137 #ifdef _WIN32
138 try {
139 nvidia.get(warnings);
140 }
141 catch (...) {
142 warnings.push_back("Caught SIGSEGV in NVIDIA GPU detection");
143 }
144 try {
145 ati.get(warnings);
146 }
147 catch (...) {
148 warnings.push_back("Caught SIGSEGV in ATI GPU detection");
149 }
150 try {
151 intel_gpu.get(warnings);
152 }
153 catch (...) {
154 warnings.push_back("Caught SIGSEGV in INTEL GPU detection");
155 }
156 try {
157 // OpenCL detection must come last
158 get_opencl(warnings);
159 }
160 catch (...) {
161 warnings.push_back("Caught SIGSEGV in OpenCL detection");
162 }
163 #else
164 void (*old_sig)(int) = signal(SIGSEGV, segv_handler);
165 if (setjmp(resume)) {
166 warnings.push_back("Caught SIGSEGV in NVIDIA GPU detection");
167 } else {
168 nvidia.get(warnings);
169 }
170
171
172 #ifndef __APPLE__ // ATI does not yet support CAL on Macs
173 if (setjmp(resume)) {
174 warnings.push_back("Caught SIGSEGV in ATI GPU detection");
175 } else {
176 ati.get(warnings);
177 }
178 #endif
179 if (setjmp(resume)) {
180 warnings.push_back("Caught SIGSEGV in INTEL GPU detection");
181 } else {
182 intel_gpu.get(warnings);
183 }
184 if (setjmp(resume)) {
185 warnings.push_back("Caught SIGSEGV in OpenCL detection");
186 } else {
187 // OpenCL detection must come last
188 get_opencl(warnings);
189 }
190 signal(SIGSEGV, old_sig);
191 #endif
192 }
193
194
correlate_gpus(bool use_all,vector<string> & descs,IGNORE_GPU_INSTANCE & ignore_gpu_instance)195 void COPROCS::correlate_gpus(
196 bool use_all,
197 vector<string> &descs,
198 IGNORE_GPU_INSTANCE &ignore_gpu_instance
199 ) {
200 unsigned int i;
201 char buf[256], buf2[256];
202
203 nvidia.correlate(use_all, ignore_gpu_instance[PROC_TYPE_NVIDIA_GPU]);
204 ati.correlate(use_all, ignore_gpu_instance[PROC_TYPE_AMD_GPU]);
205 intel_gpu.correlate(use_all, ignore_gpu_instance[PROC_TYPE_INTEL_GPU]);
206 correlate_opencl(use_all, ignore_gpu_instance);
207
208 // NOTE: OpenCL can report a max of only 4GB.
209 //
210 for (i=0; i<cpu_opencls.size(); i++) {
211 gstate.host_info.opencl_cpu_prop[gstate.host_info.num_opencl_cpu_platforms++] = cpu_opencls[i];
212 }
213
214 for (i=0; i<nvidia_gpus.size(); i++) {
215 // This is really CUDA description
216 nvidia_gpus[i].description(buf, sizeof(buf));
217 switch(nvidia_gpus[i].is_used) {
218 case COPROC_IGNORED:
219 snprintf(buf2, sizeof(buf2),
220 "CUDA: NVIDIA GPU %d (ignored by config): %s",
221 nvidia_gpus[i].device_num, buf
222 );
223 break;
224 case COPROC_USED:
225 snprintf(buf2, sizeof(buf2),
226 "CUDA: NVIDIA GPU %d: %s",
227 nvidia_gpus[i].device_num, buf
228 );
229 break;
230 case COPROC_UNUSED:
231 default:
232 snprintf(buf2, sizeof(buf2),
233 "CUDA: NVIDIA GPU %d (not used): %s",
234 nvidia_gpus[i].device_num, buf
235 );
236
237 #ifdef __APPLE__
238 if ((nvidia_gpus[i].cuda_version >= 6050) &&
239 nvidia_gpus[i].prop.major < 2) {
240 // This will be called only if CUDA recognized and reported the GPU
241 msg_printf(NULL, MSG_USER_ALERT, "NVIDIA GPU %d: %s %s",
242 nvidia_gpus[i].device_num, nvidia_gpus[i].prop.name,
243 _("cannot be used for CUDA or OpenCL computation with CUDA driver 6.5 or later")
244 );
245 }
246 #endif
247 break;
248 }
249 descs.push_back(string(buf2));
250 }
251
252 for (i=0; i<ati_gpus.size(); i++) {
253 // This is really CAL description
254 ati_gpus[i].description(buf, sizeof(buf));
255 switch(ati_gpus[i].is_used) {
256 case COPROC_IGNORED:
257 snprintf(buf2, sizeof(buf2),
258 "CAL: ATI GPU %d (ignored by config): %s",
259 ati_gpus[i].device_num, buf
260 );
261 break;
262 case COPROC_USED:
263 snprintf(buf2, sizeof(buf2),
264 "CAL: ATI GPU %d: %s",
265 ati_gpus[i].device_num, buf
266 );
267 break;
268 case COPROC_UNUSED:
269 default:
270 snprintf(buf2, sizeof(buf2),
271 "CAL: ATI GPU %d: (not used) %s",
272 ati_gpus[i].device_num, buf
273 );
274 break;
275 }
276 descs.push_back(string(buf2));
277 }
278
279 // Create descriptions for OpenCL NVIDIA GPUs
280 //
281 for (i=0; i<nvidia_opencls.size(); i++) {
282 if (nvidia_opencls[i].warn_bad_cuda) {
283 // This will be called only if CUDA did _not_ recognize and report the GPU
284 msg_printf(NULL, MSG_USER_ALERT, "NVIDIA GPU %d: %s %s",
285 nvidia_opencls[i].device_num, nvidia_opencls[i].name,
286 _("cannot be used for CUDA or OpenCL computation with CUDA driver 6.5 or later")
287 );
288 }
289 nvidia_opencls[i].description(buf, sizeof(buf), proc_type_name(PROC_TYPE_NVIDIA_GPU));
290 descs.push_back(string(buf));
291 }
292
293 // Create descriptions for OpenCL ATI GPUs
294 //
295 for (i=0; i<ati_opencls.size(); i++) {
296 ati_opencls[i].description(buf, sizeof(buf), proc_type_name(PROC_TYPE_AMD_GPU));
297 descs.push_back(string(buf));
298 }
299
300 // Create descriptions for OpenCL Intel GPUs
301 //
302 for (i=0; i<intel_gpu_opencls.size(); i++) {
303 intel_gpu_opencls[i].description(buf, sizeof(buf), proc_type_name(PROC_TYPE_INTEL_GPU));
304 descs.push_back(string(buf));
305 }
306
307 // Create descriptions for other OpenCL GPUs
308 //
309 int max_other_coprocs = MAX_RSC-1; // coprocs[0] is reserved for CPU
310 if (have_nvidia()) max_other_coprocs--;
311 if (have_ati()) max_other_coprocs--;
312 if (have_intel_gpu()) max_other_coprocs--;
313
314 // TODO: Should we implement cc_config ignore vectors for other (future) OpenCL coprocessors?
315
316 for (i=0; i<other_opencls.size(); i++) {
317 if ((int)i > max_other_coprocs) {
318 other_opencls[i].is_used = COPROC_UNUSED;
319 }
320 other_opencls[i].description(buf, sizeof(buf), other_opencls[i].name);
321 descs.push_back(string(buf));
322 }
323
324 // Create descriptions for OpenCL CPUs
325 //
326 for (i=0; i<cpu_opencls.size(); i++) {
327 cpu_opencls[i].description(buf, sizeof(buf));
328 descs.push_back(string(buf));
329 }
330
331 ati_gpus.clear();
332 nvidia_gpus.clear();
333 intel_gpus.clear();
334 ati_opencls.clear();
335 nvidia_opencls.clear();
336 intel_gpu_opencls.clear();
337 cpu_opencls.clear();
338 }
339
340 // This is called from CLIENT_STATE::init()
341 // after adding NVIDIA, ATI and Intel GPUs
342 // If we don't care about the order of GPUs in COPROCS::coprocs[],
343 // this code could be included at the end of COPROCS::correlate_gpus().
344 //
add_other_coproc_types()345 int COPROCS::add_other_coproc_types() {
346 int retval = 0;
347
348 for (unsigned int i=0; i<other_opencls.size(); i++) {
349 if (other_opencls[i].is_used != COPROC_USED) continue;
350 if (n_rsc >= MAX_RSC) {
351 retval = ERR_BUFFER_OVERFLOW;
352 break;
353 }
354
355 COPROC c;
356 // For device types other than NVIDIA, ATI or Intel GPU.
357 // we put each instance into a separate other_opencls element,
358 // so count=1.
359 //
360 c.count = 1;
361 c.opencl_device_count = 1;
362 c.opencl_prop = other_opencls[i];
363 c.available_ram = c.opencl_prop.global_mem_size;
364 c.device_num = c.opencl_prop.device_num;
365 c.peak_flops = c.opencl_prop.peak_flops;
366 c.have_opencl = true;
367 c.opencl_device_indexes[0] = c.opencl_prop.opencl_device_index;
368 c.opencl_device_ids[0] = c.opencl_prop.device_id;
369 c.instance_has_opencl[0] = true;
370 c.clear_usage();
371 safe_strcpy(c.type, other_opencls[i].name);
372
373 // Don't call COPROCS::add() because duplicate type is legal here
374 coprocs[n_rsc++] = c;
375
376 }
377
378 other_opencls.clear();
379 return retval;
380 }
381
set_path_to_client(char * path)382 void COPROCS::set_path_to_client(char *path) {
383 client_path = path;
384 // The path may be relative to the current directory
385 boinc_getcwd(client_dir);
386 }
387
write_coproc_info_file(vector<string> & warnings)388 int COPROCS::write_coproc_info_file(vector<string> &warnings) {
389 MIOFILE mf;
390 unsigned int i, temp;
391 FILE* f;
392
393 f = boinc_fopen(COPROC_INFO_FILENAME, "wb");
394 if (!f) return ERR_FOPEN;
395 mf.init_file(f);
396
397 mf.printf(" <coprocs>\n");
398
399 if (nvidia.have_cuda) {
400 mf.printf(" <have_cuda>1</have_cuda>\n");
401 mf.printf(" <cuda_version>%d</cuda_version>\n", nvidia.cuda_version);
402 }
403
404 for (i=0; i<ati_gpus.size(); ++i) {
405 ati_gpus[i].write_xml(mf, false);
406 }
407 for (i=0; i<nvidia_gpus.size(); ++i) {
408 temp = nvidia_gpus[i].count;
409 nvidia_gpus[i].count = 1;
410 nvidia_gpus[i].pci_infos[0] = nvidia_gpus[i].pci_info;
411 nvidia_gpus[i].write_xml(mf, false);
412 nvidia_gpus[i].count = temp;
413 }
414 for (i=0; i<intel_gpus.size(); ++i) {
415 intel_gpus[i].write_xml(mf, false);
416 }
417 for (i=0; i<ati_opencls.size(); ++i) {
418 ati_opencls[i].write_xml(mf, "ati_opencl", true);
419 }
420 for (i=0; i<nvidia_opencls.size(); ++i) {
421 nvidia_opencls[i].write_xml(mf, "nvidia_opencl", true);
422 }
423 for (i=0; i<intel_gpu_opencls.size(); ++i) {
424 intel_gpu_opencls[i].write_xml(mf, "intel_gpu_opencl", true);
425 }
426 for (i=0; i<other_opencls.size(); i++) {
427 other_opencls[i].write_xml(mf, "other_opencl", true);
428 }
429 for (i=0; i<cpu_opencls.size(); i++) {
430 cpu_opencls[i].write_xml(mf);
431 }
432 for (i=0; i<warnings.size(); ++i) {
433 mf.printf("<warning>%s</warning>\n", warnings[i].c_str());
434 }
435
436 mf.printf(" </coprocs>\n");
437 fclose(f);
438 return 0;
439 }
440
read_coproc_info_file(vector<string> & warnings)441 int COPROCS::read_coproc_info_file(vector<string> &warnings) {
442 MIOFILE mf;
443 int retval;
444 FILE* f;
445 string s;
446
447 COPROC_ATI my_ati_gpu;
448 COPROC_NVIDIA my_nvidia_gpu;
449 COPROC_INTEL my_intel_gpu;
450 OPENCL_DEVICE_PROP ati_opencl;
451 OPENCL_DEVICE_PROP nvidia_opencl;
452 OPENCL_DEVICE_PROP intel_gpu_opencl;
453 OPENCL_DEVICE_PROP other_opencl;
454 OPENCL_CPU_PROP cpu_opencl;
455
456 ati_gpus.clear();
457 nvidia_gpus.clear();
458 intel_gpus.clear();
459 ati_opencls.clear();
460 nvidia_opencls.clear();
461 intel_gpu_opencls.clear();
462 other_opencls.clear();
463 cpu_opencls.clear();
464
465 f = boinc_fopen(COPROC_INFO_FILENAME, "r");
466 if (!f) return ERR_FOPEN;
467 XML_PARSER xp(&mf);
468 mf.init_file(f);
469 if (!xp.parse_start("coprocs")) {
470 fclose(f);
471 return ERR_XML_PARSE;
472 }
473
474 while (!xp.get_tag()) {
475 if (xp.match_tag("/coprocs")) {
476 fclose(f);
477 return 0;
478 }
479
480 if (xp.parse_bool("have_cuda", nvidia.have_cuda)) continue;
481 if (xp.parse_int("cuda_version", nvidia.cuda_version)) {
482 continue;
483 }
484
485 if (xp.match_tag("coproc_ati")) {
486 retval = my_ati_gpu.parse(xp);
487 if (retval) {
488 my_ati_gpu.clear();
489 } else {
490 my_ati_gpu.device_num = (int)ati_gpus.size();
491 ati_gpus.push_back(my_ati_gpu);
492 }
493 continue;
494 }
495 if (xp.match_tag("coproc_cuda")) {
496 retval = my_nvidia_gpu.parse(xp);
497 if (retval) {
498 my_nvidia_gpu.clear();
499 } else {
500 my_nvidia_gpu.device_num = (int)nvidia_gpus.size();
501 my_nvidia_gpu.pci_info = my_nvidia_gpu.pci_infos[0];
502 memset(&my_nvidia_gpu.pci_infos[0], 0, sizeof(struct PCI_INFO));
503 nvidia_gpus.push_back(my_nvidia_gpu);
504 }
505 continue;
506 }
507 if (xp.match_tag("coproc_intel_gpu")) {
508 retval = my_intel_gpu.parse(xp);
509 if (retval) {
510 my_intel_gpu.clear();
511 } else {
512 my_intel_gpu.device_num = (int)intel_gpus.size();
513 intel_gpus.push_back(my_intel_gpu);
514 }
515 continue;
516 }
517
518 if (xp.match_tag("ati_opencl")) {
519 memset(&ati_opencl, 0, sizeof(ati_opencl));
520 retval = ati_opencl.parse(xp, "/ati_opencl");
521 if (retval) {
522 memset(&ati_opencl, 0, sizeof(ati_opencl));
523 } else {
524 ati_opencl.is_used = COPROC_IGNORED;
525 ati_opencls.push_back(ati_opencl);
526 }
527 continue;
528 }
529
530 if (xp.match_tag("nvidia_opencl")) {
531 memset(&nvidia_opencl, 0, sizeof(nvidia_opencl));
532 retval = nvidia_opencl.parse(xp, "/nvidia_opencl");
533 if (retval) {
534 memset(&nvidia_opencl, 0, sizeof(nvidia_opencl));
535 } else {
536 nvidia_opencl.is_used = COPROC_IGNORED;
537 nvidia_opencls.push_back(nvidia_opencl);
538 }
539 continue;
540 }
541
542 if (xp.match_tag("intel_gpu_opencl")) {
543 memset(&intel_gpu_opencl, 0, sizeof(intel_gpu_opencl));
544 retval = intel_gpu_opencl.parse(xp, "/intel_gpu_opencl");
545 if (retval) {
546 memset(&intel_gpu_opencl, 0, sizeof(intel_gpu_opencl));
547 } else {
548 intel_gpu_opencl.is_used = COPROC_IGNORED;
549 intel_gpu_opencls.push_back(intel_gpu_opencl);
550 }
551 continue;
552 }
553
554 if (xp.match_tag("other_opencl")) {
555 memset(&other_opencl, 0, sizeof(other_opencl));
556 retval = other_opencl.parse(xp, "/other_opencl");
557 if (retval) {
558 memset(&other_opencl, 0, sizeof(other_opencl));
559 } else {
560 other_opencl.is_used = COPROC_USED;
561 other_opencls.push_back(other_opencl);
562 }
563 continue;
564 }
565
566 if (xp.match_tag("opencl_cpu_prop")) {
567 memset(&cpu_opencl, 0, sizeof(cpu_opencl));
568 retval = cpu_opencl.parse(xp);
569 if (retval) {
570 memset(&cpu_opencl, 0, sizeof(cpu_opencl));
571 } else {
572 cpu_opencl.opencl_prop.is_used = COPROC_IGNORED;
573 cpu_opencls.push_back(cpu_opencl);
574 }
575 continue;
576 }
577
578 if (xp.parse_string("warning", s)) {
579 warnings.push_back(s);
580 continue;
581 }
582
583 // TODO: parse OpenCL info for CPU when implemented:
584 // gstate.host_info.have_cpu_opencl
585 // gstate.host_info.cpu_opencl_prop
586 }
587
588 fclose(f);
589 return ERR_XML_PARSE;
590 }
591
launch_child_process_to_detect_gpus()592 int COPROCS::launch_child_process_to_detect_gpus() {
593 #ifdef _WIN32
594 HANDLE prog;
595 #else
596 int prog;
597 #endif
598 char quoted_data_dir[MAXPATHLEN+2];
599 char data_dir[MAXPATHLEN];
600 int retval = 0;
601
602 retval = boinc_delete_file(COPROC_INFO_FILENAME);
603 if (retval) {
604 msg_printf(0, MSG_INFO,
605 "Failed to delete old %s. error code %d",
606 COPROC_INFO_FILENAME, retval
607 );
608 } else {
609 for (;;) {
610 if (!boinc_file_exists(COPROC_INFO_FILENAME)) break;
611 boinc_sleep(0.01);
612 }
613 }
614
615 boinc_getcwd(data_dir);
616
617 #ifdef _WIN32
618 strlcpy(quoted_data_dir, "\"", sizeof(quoted_data_dir));
619 strlcat(quoted_data_dir, data_dir, sizeof(quoted_data_dir));
620 strlcat(quoted_data_dir, "\"", sizeof(quoted_data_dir));
621 #else
622 strlcpy(quoted_data_dir, data_dir, sizeof(quoted_data_dir));
623 #endif
624
625 if (log_flags.coproc_debug) {
626 msg_printf(0, MSG_INFO,
627 "[coproc] launching child process at %s",
628 client_path
629 );
630 msg_printf(0, MSG_INFO,
631 "[coproc] relative to directory %s",
632 client_dir
633 );
634 msg_printf(0, MSG_INFO,
635 "[coproc] with data directory %s",
636 quoted_data_dir
637 );
638 }
639
640 int argc = 4;
641 char* const argv[5] = {
642 #ifdef _WIN32
643 const_cast<char *>("boinc.exe"),
644 #else
645 const_cast<char *>("boinc"),
646 #endif
647 const_cast<char *>("--detect_gpus"),
648 const_cast<char *>("--dir"),
649 const_cast<char *>(quoted_data_dir),
650 NULL
651 };
652
653 chdir(client_dir);
654
655 retval = run_program(
656 client_dir,
657 client_path,
658 argc,
659 argv,
660 #ifdef _DEBUG
661 1,
662 #else
663 0,
664 #endif
665 prog
666 );
667
668 chdir(data_dir);
669
670 if (retval) {
671 if (log_flags.coproc_debug) {
672 msg_printf(0, MSG_INFO,
673 "[coproc] run_program of child process returned error %d",
674 retval
675 );
676 }
677 return retval;
678 }
679
680 retval = get_exit_status(prog);
681 if (retval) {
682 msg_printf(0, MSG_INFO,
683 "GPU detection failed. error code %d",
684 retval
685 );
686 }
687
688 return 0;
689 }
690
691 // print descriptions of coprocs specified in cc_config.xml,
692 // and make sure counts are <= 64
693 //
bound_counts()694 void COPROCS::bound_counts() {
695 for (int j=1; j<n_rsc; j++) {
696 msg_printf(NULL, MSG_INFO, "Coprocessor specified in cc_config.xml. Type %s (%s); count %d",
697 coprocs[j].type,
698 coprocs[j].non_gpu?"non-GPU":"GPU",
699 coprocs[j].count
700 );
701 if (coprocs[j].count > MAX_COPROC_INSTANCES) {
702 msg_printf(NULL, MSG_USER_ALERT,
703 "%d instances of %s specified in cc_config.xml; max is %d",
704 coprocs[j].count,
705 coprocs[j].type,
706 MAX_COPROC_INSTANCES
707 );
708 coprocs[j].count = MAX_COPROC_INSTANCES;
709 }
710 }
711 }
712