Пример #1
0
inline DB_ID_TYPE host_usage_to_gavid(HOST_USAGE& hu, APP& app) {
    return app.id*1000000 - hu.resource_type();
}
Пример #2
0
bool PLAN_CLASS_SPEC::check(SCHEDULER_REQUEST& sreq, HOST_USAGE& hu) {
    COPROC* cpp = NULL;
    bool can_use_multicore = true;

    hu.sequential_app(sreq.host.p_fpops);

    // CPU features
    //
    // older clients report CPU features in p_model,
    // within square brackets
    //
    // the requested features are surrounded by spaces,
    // so we can look for them with strstr()
    //
    if (!cpu_features.empty()) {
        char buf[8192], buf2[512];
        sprintf(buf, " %s ", sreq.host.p_features);
        char* p = strrchr(sreq.host.p_model, '[');
        if (p) {
            sprintf(buf2, " %s", p+1);
            p = strchr(buf2, ']');
            if (p) {
                *p = 0;
            }
            strcat(buf2, " ");
            strcat(buf, buf2);
        }
        downcase_string(buf);

        for (unsigned int i=0; i<cpu_features.size(); i++) {
            if (!strstr(buf, cpu_features[i].c_str())) {
                if (config.debug_version_select) {
                    log_messages.printf(MSG_NORMAL,
                        "[version] plan_class_spec: CPU lacks feature '%s' (got '%s')\n",
                        cpu_features[i].c_str(), sreq.host.p_features
                    );
                }
                return false;
            }
        }
    }

    // min NCPUS
    //
    if (min_ncpus && g_wreq->effective_ncpus < min_ncpus) {
        if (config.debug_version_select) {
            log_messages.printf(MSG_NORMAL,
                "[version] plan_class_spec: not enough CPUs: %d < %f\n",
                g_wreq->effective_ncpus, min_ncpus
            );
        }
        return false;
    }

    // OS version
    //
    if (have_os_regex && regexec(&(os_regex), sreq.host.os_version, 0, NULL, 0)) {
        if (config.debug_version_select) {
            log_messages.printf(MSG_NORMAL,
                "[version] plan_class_spec: OS version '%s' didn't match regexp\n",
                sreq.host.os_version
            );
        }
        return false;
    }

    // BOINC versions 
    //
    if (min_core_client_version && sreq.core_client_version < min_core_client_version) {
        if (config.debug_version_select) {
            log_messages.printf(MSG_NORMAL,
                "[version] plan_class_spec: Need newer BOINC core client: %d < %d\n",
                sreq.core_client_version, min_core_client_version
            );
        }
        add_no_work_message("A newer BOINC may be required for some tasks.");
        return false;
    }
    if (max_core_client_version && sreq.core_client_version > max_core_client_version) {
        if (config.debug_version_select) {
            log_messages.printf(MSG_NORMAL,
                "[version] plan_class_spec: Need older BOINC core client: %d > %d\n",
                sreq.core_client_version, max_core_client_version
            );
        }
        return false;
    }

    if (virtualbox) {

        // host must run 7.0+ client
        //
        if (sreq.core_client_major_version < 7) {
            add_no_work_message("BOINC client 7.0+ required for Virtualbox jobs");
            return false;
        }

        // host must have VirtualBox 3.2 or later
        //
        if (strlen(sreq.host.virtualbox_version) == 0) {
            add_no_work_message("VirtualBox is not installed");
            return false;
        }
        int n, maj, min, rel;
        n = sscanf(sreq.host.virtualbox_version, "%d.%d.%d", &maj, &min, &rel);
        if (n != 3) {
            if (config.debug_version_select) {
                log_messages.printf(MSG_NORMAL,
                    "[version] plan_class_spec: can't parse vbox version\n"
                );
            }
            return false;
        }
        int v = maj*10000 + min*100 + rel;
        if (min_vbox_version && v < min_vbox_version) {
            if (config.debug_version_select) {
                log_messages.printf(MSG_NORMAL,
                    "[version] plan_class_spec: vbox version too low: %d < %d\n",
                    v, min_vbox_version
                );
            }
            return false;
        }
        if (max_vbox_version && v > max_vbox_version) {
            if (config.debug_version_select) {
                log_messages.printf(MSG_NORMAL,
                    "[version] plan_class_spec: vbox version too high: %d > %d\n",
                    v, max_vbox_version
                );
            }
            return false;
        }

        // host must have VM acceleration in order to run multi-core jobs
        //
        if (max_threads > 1) {
            if ((!strstr(sreq.host.p_features, "vmx") && !strstr(sreq.host.p_features, "svm"))
                || sreq.host.p_vm_extensions_disabled
            ) {
                can_use_multicore = false;
            }
        }

        // only send the version for host's primary platform.
        // A Win64 host can't run a 32-bit VM app:
        // it will look in the 32-bit half of the registry and fail
        //
        PLATFORM* p = g_request->platforms.list[0];
        if (is_64b_platform(p->name)) {
            if (!is64bit) return false;
        } else {
            if (is64bit) return false;
        }
    }

    // project-specific preference
    //
    if (have_project_prefs_regex && strlen(project_prefs_tag)) {
        char tag[256], value[256];
        char buf[65536];
        extract_venue(g_reply->user.project_prefs, g_reply->host.venue, buf);
        sprintf(tag,"<%s>",project_prefs_tag);
        bool p = parse_str(buf, tag, value, sizeof(value));
        if (config.debug_version_select) {
            log_messages.printf(MSG_NORMAL,
                "[version] plan_class_spec: parsed project prefs setting '%s' : %s\n",
                project_prefs_tag, p?"true":"false"
            );
        }
        if (regexec(&(project_prefs_regex), value, 0, NULL, 0)) {
            if (config.debug_version_select) {
                log_messages.printf(MSG_NORMAL,
                    "[version] plan_class_spec: project prefs setting '%s' prevents using plan class.\n",
                    project_prefs_tag
                );
            }
            return false;
        }
    }

    double gpu_ram = 0;
    int driver_version = 0;
    double gpu_utilization = 1.0;

    // user defined gpu_utilization
    //
    if (strlen(gpu_utilization_tag)) {
        char tag[256];
        char buf[65536];
        double v = 0;
        extract_venue(g_reply->user.project_prefs, g_reply->host.venue, buf);
        sprintf(tag,"<%s>",gpu_utilization_tag);
        bool p = parse_double(buf, tag, v);
        if (config.debug_version_select) {
            log_messages.printf(MSG_NORMAL,
                "[version] plan_class_spec: parsed project prefs setting '%s' : %s : %f\n",
                gpu_utilization_tag, p?"true":"false", v
            );
        }
        if (v) {
            gpu_utilization = v;
        }
    }

    // AMD
    //
    if (!strcmp(gpu_type, "amd") || !strcmp(gpu_type, "ati")) {
        COPROC_ATI& cp = sreq.coprocs.ati;
        cpp = &cp;

        if (!cp.count) {
            if (config.debug_version_select) {
                log_messages.printf(MSG_NORMAL,
                    "[version] plan_class_spec: No AMD GPUs found\n"
                );
            }
            return false;
        }
        if (min_gpu_ram_mb) {
            gpu_requirements[PROC_TYPE_AMD_GPU].update(0, min_gpu_ram_mb * MEGA);
        }
        if (min_driver_version) {
            gpu_requirements[PROC_TYPE_AMD_GPU].update(abs(min_driver_version), 0);
        }

        if (need_ati_libs) {
            if (!cp.atirt_detected) {
	        if (config.debug_version_select) {
                    log_messages.printf(MSG_NORMAL,
                        "[version] plan_class_spec: ATI libraries not found\n"
                    );
                }
                return false;
            }
        } else {
            if (!cp.amdrt_detected) {
	        if (config.debug_version_select) {
                    log_messages.printf(MSG_NORMAL,
                        "[version] plan_class_spec: AMD libraries not found\n"
                    );
                }
                return false;
            }
        }

        if (min_cal_target && cp.attribs.target < min_cal_target) {
            if (config.debug_version_select) {
                log_messages.printf(MSG_NORMAL,
                    "[version] plan_class_spec: CAL target less than minimum (%d < %d)\n",
                    cp.attribs.target, min_cal_target
                );
            }
            return false;
        }
        if (max_cal_target && cp.attribs.target > max_cal_target) {
            if (config.debug_version_select) {
                log_messages.printf(MSG_NORMAL,
                    "[version] plan_class_spec: CAL target greater than maximum (%d > %d)\n",
                    cp.attribs.target, max_cal_target
                );
            }
            return false;
        }

        cp.set_peak_flops();
        gpu_ram = cp.opencl_prop.global_mem_size;

        driver_version = 0;
        if (cp.have_cal) {
            int major, minor, release, scanned;
            scanned = sscanf(cp.version, "%d.%d.%d", &major, &minor, &release);
            if (scanned != 3) {
                if (config.debug_version_select) {
                    log_messages.printf(MSG_NORMAL,
                        "[version] plan_class_spec: driver version '%s' couldn't be parsed\n",
                        cp.version
                    );
                }
                return false;
            } else {
                driver_version = ati_version_int(major,minor,release);
            }
        } else {
            if (config.debug_version_select) {
                log_messages.printf(MSG_NORMAL,
                    "[version] plan_class_spec: no CAL, driver version couldn't be determined\n"
                );
            }
        }

    // NVIDIA
    //
    } else if (!strcmp(gpu_type, "nvidia")) {
        COPROC_NVIDIA& cp = sreq.coprocs.nvidia;
        cpp = &cp;

        if (!cp.count) {
            if (config.debug_version_select) {
                log_messages.printf(MSG_NORMAL,
                    "[version] plan_class_spec: No NVIDIA GPUs found\n"
                );
            }
            return false;
        }

        if (min_gpu_ram_mb) {
            gpu_requirements[PROC_TYPE_NVIDIA_GPU].update(0, min_gpu_ram_mb * MEGA);
        }
        if (min_driver_version) {
            gpu_requirements[PROC_TYPE_NVIDIA_GPU].update(abs(min_driver_version), 0);
        }
        // compute capability
        int v = (cp.prop.major)*100 + cp.prop.minor;
        if (min_nvidia_compcap && min_nvidia_compcap > v) {
            if (config.debug_version_select) {
                log_messages.printf(MSG_NORMAL,
                    "[version] plan_class_spec: NVIDIA compute capability required min: %d, supplied: %d\n",
                    min_nvidia_compcap, v
                );
            }
            return false;
        }
        if (max_nvidia_compcap && max_nvidia_compcap < v) {
            if (config.debug_version_select) {
                log_messages.printf(MSG_NORMAL,
                    "[version] plan_class_spec: CUDA compute capability required max: %d, supplied: %d\n",
                    max_nvidia_compcap, v
                );
            }
            return false;
        }
        if (cuda) {
            // CUDA version
            if (min_cuda_version && min_cuda_version > cp.cuda_version) {
                if (config.debug_version_select) {
                    log_messages.printf(MSG_NORMAL,
                        "[version] plan_class_spec: CUDA version required min: %d, supplied: %d\n",
                        min_cuda_version, cp.cuda_version
                    );
                }
                return false;
            }
            if (max_cuda_version && max_cuda_version < cp.cuda_version) {
                if (config.debug_version_select) {
                    log_messages.printf(MSG_NORMAL,
                        "[version] plan_class_spec: CUDA version required max: %d, supplied: %d\n",
                        max_cuda_version, cp.cuda_version
                    );
                }
                return false;
            }
        }
        gpu_ram = cp.prop.totalGlobalMem;
        cp.set_peak_flops();

    // Intel GPU
    //
    } else if (!strcmp(gpu_type, "intel")) {
        COPROC& cp = sreq.coprocs.intel_gpu;
        cpp = &cp;

        if (!cp.count) {
            if (config.debug_version_select) {
                log_messages.printf(MSG_NORMAL,
                    "[version] [version] No Intel GPUs found\n"
                );
            }
            return false;
        }
        if (min_gpu_ram_mb) {
            gpu_requirements[PROC_TYPE_INTEL_GPU].update(0, min_gpu_ram_mb * MEGA);
        }
    }

    if (opencl) {
        // check for OpenCL at all
        if (!cpp->have_opencl) {
            if (config.debug_version_select) {
                log_messages.printf(MSG_NORMAL,
                    "[version] GPU doesn't support OpenCL\n"
                );
            }
            return false;
        }

        // OpenCL device version
        //
        if (min_opencl_version && min_opencl_version > cpp->opencl_prop.opencl_device_version_int) {
            if (config.debug_version_select) {
                log_messages.printf(MSG_NORMAL,
                    "[version] OpenCL device version required min: %d, supplied: %d\n",
                    min_opencl_version, cpp->opencl_prop.opencl_device_version_int
                );
            }
            return false;
        }

        gpu_ram = cpp->opencl_prop.global_mem_size;
    }

    // general GPU
    //
    if (strlen(gpu_type)) {

        // GPU RAM
        if (min_gpu_ram_mb && min_gpu_ram_mb * MEGA > gpu_ram) {
            if (config.debug_version_select) {
                log_messages.printf(MSG_NORMAL,
                    "[version] plan_class_spec: GPU RAM required min: %f, supplied: %f\n",
                    min_gpu_ram_mb * MEGA, gpu_ram
                );
            }
            return false;
        }

        // (display) driver version
        if (min_driver_version && driver_version) {
            if (min_driver_version > driver_version) {
                if (config.debug_version_select) {
                    log_messages.printf(MSG_NORMAL,
                        "[version] plan_class_spec: driver version required min: %d, supplied: %d\n",
                        abs(min_driver_version), driver_version
                    );
                }
                return false;
            }
        }
        if (max_driver_version && driver_version) {
            if (max_driver_version < driver_version) {
                if (config.debug_version_select) {
                    log_messages.printf(MSG_NORMAL,
                        "[version] plan_class_spec: driver version required max: %d, supplied: %d\n",
                        abs(max_driver_version), driver_version
                    );
                }
                return false;
            }
        }

        hu.gpu_ram = (gpu_ram_used_mb?gpu_ram_used_mb:min_gpu_ram_mb) * MEGA;

        double gpu_usage;

        // if ngpus < 0, set gpu_usage by the fraction of the total
        // video RAM a tasks would take
        // i.e. fill the device memory with tasks
        //
        if (ngpus < 0) {
            gpu_usage = (floor(gpu_ram/ hu.gpu_ram) * hu.gpu_ram) / gpu_ram ;
        } else if (ngpus > 0) {
            gpu_usage = ngpus * gpu_utilization;
        } else {
            gpu_usage = gpu_utilization;
        }

        coproc_perf(
            capped_host_fpops(),
            gpu_peak_flops_scale * gpu_usage * cpp->peak_flops,
            cpu_frac,
            hu.projected_flops,
            hu.avg_ncpus
        );
        if (avg_ncpus) {
            hu.avg_ncpus = avg_ncpus;
        }
        // I believe the first term here is just hu.projected_flops,
        // but I'm leaving it spelled out to match GPU scheduling 
        // code in sched_customize.cpp
        //
        hu.peak_flops = gpu_peak_flops_scale*gpu_usage*cpp->peak_flops + hu.avg_ncpus*capped_host_fpops();

        if (!strcmp(gpu_type, "amd") || !strcmp(gpu_type, "ati")) {
            hu.proc_type = PROC_TYPE_AMD_GPU;
            hu.gpu_usage = gpu_usage;
        } else if (!strcmp(gpu_type, "nvidia")) {
            hu.proc_type = PROC_TYPE_NVIDIA_GPU;
            hu.gpu_usage = gpu_usage;
        } else if (!strcmp(gpu_type, "intel")) {
            hu.proc_type = PROC_TYPE_INTEL_GPU;
            hu.gpu_usage = gpu_usage;
        }

    // CPU only
    //
    } else {
        if (avg_ncpus) {
            hu.avg_ncpus = avg_ncpus;
        } else {
            if (can_use_multicore) {
                if (max_threads > g_wreq->effective_ncpus) {
                    hu.avg_ncpus = g_wreq->effective_ncpus;
                } else {
                    hu.avg_ncpus = max_threads;
                }
            } else {
                hu.avg_ncpus = 1;
            }
        }

        hu.peak_flops = capped_host_fpops() * hu.avg_ncpus;
        hu.projected_flops = capped_host_fpops() * hu.avg_ncpus * projected_flops_scale;
    }
    hu.max_ncpus = hu.avg_ncpus;

    if (config.debug_version_select) {
        log_messages.printf(MSG_NORMAL,
            "[version] plan_class_spec: host_flops: %e, \tscale: %.2f, \tprojected_flops: %e, \tpeak_flops: %e\n",
            sreq.host.p_fpops, projected_flops_scale, hu.projected_flops,
            hu.peak_flops
        );
    }

    return true;

}
Пример #3
0
bool PLAN_CLASS_SPEC::check(SCHEDULER_REQUEST& sreq, HOST_USAGE& hu) {
    COPROC* cpp = NULL;
    bool can_use_multicore = true;

    hu.sequential_app(sreq.host.p_fpops);

    // CPU features
    //
    // older clients report CPU features in p_model,
    // within square brackets
    //
    // the requested features are surrounded by spaces,
    // so we can look for them with strstr()
    //
    if (!cpu_features.empty()) {
        char buf[8192], buf2[512];
        sprintf(buf, " %s ", sreq.host.p_features);
        char* p = strrchr(sreq.host.p_model, '[');
        if (p) {
            sprintf(buf2, " %s", p+1);
            p = strchr(buf2, ']');
            if (p) {
                *p = 0;
            }
            strcat(buf2, " ");
            strcat(buf, buf2);
        }
        downcase_string(buf);

        for (unsigned int i=0; i<cpu_features.size(); i++) {
            if (!strstr(buf, cpu_features[i].c_str())) {
                if (config.debug_version_select) {
                    log_messages.printf(MSG_NORMAL,
                        "[version] plan_class_spec: CPU lacks feature '%s' (got '%s')\n",
                        cpu_features[i].c_str(), sreq.host.p_features
                    );
                }
                return false;
            }
        }
    }

    // min NCPUS
    //
    if (min_ncpus && g_wreq->effective_ncpus < min_ncpus) {
        if (config.debug_version_select) {
            log_messages.printf(MSG_NORMAL,
                "[version] plan_class_spec: not enough CPUs: %d < %f\n",
                g_wreq->effective_ncpus, min_ncpus
            );
        }
        return false;
    }

    // host summary
    //
    if (have_host_summary_regex
        && regexec(&(host_summary_regex), g_reply->host.serialnum, 0, NULL, 0)
    ) {
        if (config.debug_version_select) {
            log_messages.printf(MSG_NORMAL,
                "[version] plan_class_spec: host summary '%s' didn't match regexp\n",
                g_reply->host.serialnum
            );
        }
        return false;
    }

    // OS version
    //
    if (have_os_regex && regexec(&(os_regex), sreq.host.os_version, 0, NULL, 0)) {
        if (config.debug_version_select) {
            log_messages.printf(MSG_NORMAL,
                "[version] plan_class_spec: OS version '%s' didn't match regexp\n",
                sreq.host.os_version
            );
        }
        return false;
    }
    if (min_os_version || max_os_version) {
        double host_os_version_num = os_version_num(sreq.host);
        if (!host_os_version_num) {
            if (config.debug_version_select) {
                log_messages.printf(MSG_NORMAL,
                    "[version] plan_class_spec: Can't determine numerical OS version '%s'\n",
                    sreq.host.os_version
                );
            }
            return false;
        }
        if (min_os_version && (host_os_version_num < min_os_version)) {
            if (config.debug_version_select) {
                log_messages.printf(MSG_NORMAL,
                    "[version] plan_class_spec: OS version '%s' too low (%.0f / %.0f)\n",
                    sreq.host.os_version, host_os_version_num, min_os_version
                );
            }
            return false;
        }
        if (max_os_version && (host_os_version_num > max_os_version)) {
            if (config.debug_version_select) {
                log_messages.printf(MSG_NORMAL,
                    "[version] plan_class_spec: OS version '%s' too high (%.0f / %.0f)\n",
                    sreq.host.os_version, host_os_version_num, max_os_version
                );
            }
            return false;
        }
    }
    if (min_android_version || max_android_version) {
        if (strcasecmp(sreq.host.os_name, "android")) return false;
        int host_android_version = android_version_num(sreq.host);
        if (!host_android_version) {
            if (config.debug_version_select) {
                log_messages.printf(MSG_NORMAL,
                    "[version] plan_class_spec: Can't determine numerical Android version '%s'\n",
                    sreq.host.os_version
                );
            }
            if (min_android_version>0) return false;
        }
        if (min_android_version && (host_android_version < min_android_version)) {
            if (config.debug_version_select) {
                log_messages.printf(MSG_NORMAL,
                    "[version] plan_class_spec: Android version '%s' too low (%d / %d)\n",
                    sreq.host.os_version, host_android_version, min_android_version
                );
            }
            return false;
        }
        if (max_android_version && (host_android_version > max_android_version)) {
            if (config.debug_version_select) {
                log_messages.printf(MSG_NORMAL,
                    "[version] plan_class_spec: Android version '%s' too high (%d / %d)\n",
                    sreq.host.os_version, host_android_version, max_android_version
                );
            }
            return false;
        }
    }

    // CPU vendor
    //
    if (have_cpu_vendor_regex && regexec(&(cpu_vendor_regex), sreq.host.p_vendor, 0, NULL, 0)) {
        if (config.debug_version_select) {
            log_messages.printf(MSG_NORMAL,
                "[version] plan_class_spec: CPU vendor '%s' didn't match regexp\n",
                sreq.host.p_vendor
            );
        }
        return false;
    }

    // BOINC versions
    //
    if (min_core_client_version && sreq.core_client_version < min_core_client_version) {
        if (config.debug_version_select) {
            log_messages.printf(MSG_NORMAL,
                "[version] plan_class_spec: Need newer BOINC core client: %d < %d\n",
                sreq.core_client_version, min_core_client_version
            );
        }
        add_no_work_message("A newer BOINC may be required for some tasks.");
        return false;
    }
    if (max_core_client_version && sreq.core_client_version > max_core_client_version) {
        if (config.debug_version_select) {
            log_messages.printf(MSG_NORMAL,
                "[version] plan_class_spec: Need older BOINC core client: %d > %d\n",
                sreq.core_client_version, max_core_client_version
            );
        }
        return false;
    }

    if (virtualbox) {

        // host must run 7.0+ client
        //
        if (sreq.core_client_major_version < 7) {
            add_no_work_message("BOINC client 7.0+ required for Virtualbox jobs");
            return false;
        }

        // host must have VirtualBox 3.2 or later
        //
        if (strlen(sreq.host.virtualbox_version) == 0) {
            add_no_work_message("VirtualBox is not installed");
            return false;
        }
        int n, maj, min, rel;
        n = sscanf(sreq.host.virtualbox_version, "%d.%d.%d", &maj, &min, &rel);
        if (n != 3) {
            if (config.debug_version_select) {
                log_messages.printf(MSG_NORMAL,
                    "[version] plan_class_spec: can't parse vbox version\n"
                );
            }
            return false;
        }
        int v = maj*10000 + min*100 + rel;
        if (min_vbox_version && v < min_vbox_version) {
            if (config.debug_version_select) {
                log_messages.printf(MSG_NORMAL,
                    "[version] plan_class_spec: vbox version too low: %d < %d\n",
                    v, min_vbox_version
                );
            }
            return false;
        }
        if (max_vbox_version && v > max_vbox_version) {
            if (config.debug_version_select) {
                log_messages.printf(MSG_NORMAL,
                    "[version] plan_class_spec: vbox version too high: %d > %d\n",
                    v, max_vbox_version
                );
            }
            return false;
        }
        if (in_vector(v, exclude_vbox_version)) {
            if (config.debug_version_select) {
                log_messages.printf(MSG_NORMAL,
                    "[version] plan_class_spec: vbox version %d excluded\n", v
                );
            }
            return false;
        }

        if (vm_accel_required) {
            if ((!strstr(sreq.host.p_features, "vmx") && !strstr(sreq.host.p_features, "svm"))
                || sreq.host.p_vm_extensions_disabled
            ) {
                if (config.debug_version_select) {
                    log_messages.printf(MSG_NORMAL,
                        "[version] plan_class_spec: missing VM HW acceleration\n"
                    );
                }
                return false;
            }
        }

        // host must have VM acceleration in order to run multi-core jobs
        //
        if (max_threads > 1) {
            if ((!strstr(sreq.host.p_features, "vmx") && !strstr(sreq.host.p_features, "svm"))
                || sreq.host.p_vm_extensions_disabled
            ) {
                can_use_multicore = false;
            }
        }

        // only send the version for host's primary platform.
        // A Win64 host can't run a 32-bit VM app:
        // it will look in the 32-bit half of the registry and fail
        //
        PLATFORM* p = g_request->platforms.list[0];
        if (is_64b_platform(p->name)) {
            if (!is64bit) return false;
        } else {
            if (is64bit) return false;
        }
    }

    // project-specific preference
    //
    if (have_project_prefs_regex && strlen(project_prefs_tag)) {
        char tag[256], value[256];
        char buf[65536];
        extract_venue(g_reply->user.project_prefs, g_reply->host.venue, buf, sizeof(buf));
        sprintf(tag,"<%s>",project_prefs_tag);
        bool p = parse_str(buf, tag, value, sizeof(value));
        if (config.debug_version_select) {
            log_messages.printf(MSG_NORMAL,
                "[version] plan_class_spec: parsed project prefs setting '%s' : %s\n",
                project_prefs_tag, p?"true":"false"
            );
        }
        if (p ? regexec(&(project_prefs_regex), value, 0, NULL, 0) : !project_prefs_default_true) {
            if (config.debug_version_select) {
                log_messages.printf(MSG_NORMAL,
                    "[version] plan_class_spec: project prefs setting '%s' value='%s' prevents using plan class.\n",
                    project_prefs_tag, p ? value : "(tag missing)"
                );
            }
            return false;
        }
    }

    double gpu_ram = 0;
    int driver_version = 0;
    double gpu_utilization = 1.0;

    // user defined gpu_utilization
    //
    if (strlen(gpu_utilization_tag)) {
        char tag[256];
        char buf[65536];
        double v = 0;
        extract_venue(g_reply->user.project_prefs, g_reply->host.venue, buf, sizeof(buf));
        sprintf(tag,"<%s>",gpu_utilization_tag);
        bool p = parse_double(buf, tag, v);
        if (config.debug_version_select) {
            log_messages.printf(MSG_NORMAL,
                "[version] plan_class_spec: parsed project prefs setting '%s' : %s : %f\n",
                gpu_utilization_tag, p?"true":"false", v
            );
        }
        if (v) {
            gpu_utilization = v;
        }
    }

    // AMD
    //
    if (!strcmp(gpu_type, "amd") || !strcmp(gpu_type, "ati")) {
        COPROC_ATI& cp = sreq.coprocs.ati;
        cpp = &cp;

        if (!cp.count) {
            if (config.debug_version_select) {
                log_messages.printf(MSG_NORMAL,
                    "[version] plan_class_spec: No AMD GPUs found\n"
                );
            }
            return false;
        }
        if (min_gpu_ram_mb) {
            gpu_requirements[PROC_TYPE_AMD_GPU].update(0, min_gpu_ram_mb * MEGA);
        }
        if (min_driver_version) {
            gpu_requirements[PROC_TYPE_AMD_GPU].update(abs(min_driver_version), 0);
        }

        if (need_ati_libs) {
            if (!cp.atirt_detected) {
                if (config.debug_version_select) {
                    log_messages.printf(MSG_NORMAL,
                        "[version] plan_class_spec: ATI libraries not found\n"
                    );
                }
                return false;
            }
        } else {
            if (need_amd_libs && !cp.amdrt_detected) {
                if (config.debug_version_select) {
                    log_messages.printf(MSG_NORMAL,
                        "[version] plan_class_spec: AMD libraries not found\n"
                    );
                }
                return false;
            }
        }

        if (without_opencl) {
            if (cp.have_opencl) {
                if (config.debug_version_select) {
                    log_messages.printf(MSG_NORMAL,
                        "[version] plan_class_spec: OpenCL detected. Plan restricted to CAL only GPUs\n"
                    );
                }
                return false;
            }
        }


        if (min_cal_target && cp.attribs.target < min_cal_target) {
            if (config.debug_version_select) {
                log_messages.printf(MSG_NORMAL,
                    "[version] plan_class_spec: CAL target less than minimum (%d < %d)\n",
                    cp.attribs.target, min_cal_target
                );
            }
            return false;
        }
        if (max_cal_target && cp.attribs.target > max_cal_target) {
            if (config.debug_version_select) {
                log_messages.printf(MSG_NORMAL,
                    "[version] plan_class_spec: CAL target greater than maximum (%d > %d)\n",
                    cp.attribs.target, max_cal_target
                );
            }
            return false;
        }

        cp.set_peak_flops();
        gpu_ram = cp.opencl_prop.global_mem_size;

        driver_version = 0;
        if (cp.have_cal) {
            int major, minor, release, scanned;
            scanned = sscanf(cp.version, "%d.%d.%d", &major, &minor, &release);
            if (scanned != 3) {
                if (config.debug_version_select) {
                    log_messages.printf(MSG_NORMAL,
                        "[version] plan_class_spec: driver version '%s' couldn't be parsed\n",
                        cp.version
                    );
                }
                return false;
            } else {
                driver_version = ati_version_int(major,minor,release);
            }
        } else {
            if (config.debug_version_select) {
                log_messages.printf(MSG_NORMAL,
                    "[version] plan_class_spec: no CAL, driver version couldn't be determined\n"
                );
            }
        }

    // NVIDIA
    //
    } else if (!strcmp(gpu_type, "nvidia")) {
        COPROC_NVIDIA& cp = sreq.coprocs.nvidia;
        cpp = &cp;

        if (!cp.count) {
            if (config.debug_version_select) {
                log_messages.printf(MSG_NORMAL,
                    "[version] plan_class_spec: No NVIDIA GPUs found\n"
                );
            }
            return false;
        }
        
        // in analogy to ATI/AMD 
        driver_version=cp.display_driver_version;

        if (min_gpu_ram_mb) {
            gpu_requirements[PROC_TYPE_NVIDIA_GPU].update(0, min_gpu_ram_mb * MEGA);
        }
        if (min_driver_version) {
            gpu_requirements[PROC_TYPE_NVIDIA_GPU].update(abs(min_driver_version), 0);
        }
        // compute capability
        int v = (cp.prop.major)*100 + cp.prop.minor;
        if (min_nvidia_compcap && min_nvidia_compcap > v) {
            if (config.debug_version_select) {
                log_messages.printf(MSG_NORMAL,
                    "[version] plan_class_spec: NVIDIA compute capability required min: %d, supplied: %d\n",
                    min_nvidia_compcap, v
                );
            }
            return false;
        }
        if (max_nvidia_compcap && max_nvidia_compcap < v) {
            if (config.debug_version_select) {
                log_messages.printf(MSG_NORMAL,
                    "[version] plan_class_spec: CUDA compute capability required max: %d, supplied: %d\n",
                    max_nvidia_compcap, v
                );
            }
            return false;
        }
        if (cuda) {
            // CUDA version
            if (min_cuda_version && min_cuda_version > cp.cuda_version) {
                if (config.debug_version_select) {
                    log_messages.printf(MSG_NORMAL,
                        "[version] plan_class_spec: CUDA version required min: %d, supplied: %d\n",
                        min_cuda_version, cp.cuda_version
                    );
                }
                return false;
            }
            if (max_cuda_version && max_cuda_version < cp.cuda_version) {
                if (config.debug_version_select) {
                    log_messages.printf(MSG_NORMAL,
                        "[version] plan_class_spec: CUDA version required max: %d, supplied: %d\n",
                        max_cuda_version, cp.cuda_version
                    );
                }
                return false;
            }
        }
        gpu_ram = cp.prop.totalGlobalMem;
        cp.set_peak_flops();

    // Intel GPU
    //
    } else if (strstr(gpu_type, "intel") == gpu_type) {
        COPROC& cp = sreq.coprocs.intel_gpu;
        cpp = &cp;

        if (!cp.count) {
            if (config.debug_version_select) {
                log_messages.printf(MSG_NORMAL,
                    "[version] plan_class_spec: No Intel GPUs found\n"
                );
            }
            return false;
        }
        if (min_gpu_ram_mb) {
            gpu_requirements[PROC_TYPE_INTEL_GPU].update(0, min_gpu_ram_mb * MEGA);
        }

    // custom GPU type
    //
    } else if (strlen(gpu_type)) {
        cpp = sreq.coprocs.lookup_type(gpu_type);
        if (!cpp) {
            if (config.debug_version_select) {
                log_messages.printf(MSG_NORMAL,
                    "[version] plan_class_spec: No %s found\n", gpu_type
                );
            }
            return false;
        }
        if (config.debug_version_select) {
            log_messages.printf(MSG_NORMAL,
                "[version] plan_class_spec: Custom coproc %s found\n", gpu_type
            );
        }
    }

    if (opencl) {
        if (cpp) {
            if (!cpp->have_opencl) {
                if (config.debug_version_select) {
                    log_messages.printf(MSG_NORMAL,
                        "[version] GPU doesn't support OpenCL\n"
                    );
                }
                return false;
            }
            if (!opencl_check(cpp->opencl_prop)) {
                return false;
            }
            gpu_ram = cpp->opencl_prop.global_mem_size;
        } else {
            // OpenCL CPU app version.
            // The host may have several OpenCL CPU libraries.
            // See if any of them works.
            // TODO: there should be a way of saying which library
            // the app version requires,
            // or a way of conveying to the app which one to use.
            //
            bool found = false;
            for (int i=0; i<sreq.host.num_opencl_cpu_platforms; i++) {
                if (opencl_check(sreq.host.opencl_cpu_prop[i].opencl_prop)) {
                    found = true;
                    break;
                }
            }
            if (!found) {
                if (config.debug_version_select) {
                    log_messages.printf(MSG_NORMAL,
                        "[version] CPU doesn't support OpenCL\n"
                    );
                }
                return false;
            }
        }
    }

    // general GPU
    //
    if (strlen(gpu_type)) {

        // GPU RAM
        if (min_gpu_ram_mb && min_gpu_ram_mb * MEGA > gpu_ram) {
            if (config.debug_version_select) {
                log_messages.printf(MSG_NORMAL,
                    "[version] plan_class_spec: GPU RAM required min: %f, supplied: %f\n",
                    min_gpu_ram_mb * MEGA, gpu_ram
                );
            }
            return false;
        }

        // (display) driver version
        if (min_driver_version && driver_version) {
            if (min_driver_version > driver_version) {
                if (config.debug_version_select) {
                    log_messages.printf(MSG_NORMAL,
                        "[version] plan_class_spec: driver version required min: %d, supplied: %d\n",
                        abs(min_driver_version), driver_version
                    );
                }
                return false;
            }
        }
        if (max_driver_version && driver_version) {
            if (max_driver_version < driver_version) {
                if (config.debug_version_select) {
                    log_messages.printf(MSG_NORMAL,
                        "[version] plan_class_spec: driver version required max: %d, supplied: %d\n",
                        abs(max_driver_version), driver_version
                    );
                }
                return false;
            }
        }

        hu.gpu_ram = (gpu_ram_used_mb?gpu_ram_used_mb:min_gpu_ram_mb) * MEGA;

        double gpu_usage;

        // if ngpus < 0, set gpu_usage by the fraction of the total
        // video RAM a tasks would take
        // i.e. fill the device memory with tasks
        //
        if (ngpus < 0) {
            gpu_usage = (floor(gpu_ram/ hu.gpu_ram) * hu.gpu_ram) / gpu_ram ;
        } else if (ngpus > 0) {
            gpu_usage = ngpus * gpu_utilization;
        } else {
            gpu_usage = gpu_utilization;
        }

        // if we don't know GPU peak flops, treat it like a CPU app
        //
        if (cpp->peak_flops == 0) {
            strcpy(hu.custom_coproc_type, gpu_type);
            hu.avg_ncpus = cpu_frac;
            hu.gpu_usage = gpu_usage;
        } else {
            coproc_perf(
                capped_host_fpops(),
                gpu_peak_flops_scale * gpu_usage * cpp->peak_flops,
                cpu_frac,
                hu.projected_flops,
                hu.avg_ncpus
            );
            if (avg_ncpus) {
                hu.avg_ncpus = avg_ncpus;
            }
            // I believe the first term here is just hu.projected_flops,
            // but I'm leaving it spelled out to match GPU scheduling 
            // code in sched_customize.cpp
            //
            hu.peak_flops = gpu_peak_flops_scale*gpu_usage*cpp->peak_flops + hu.avg_ncpus*capped_host_fpops();
        }

        if (!strcmp(gpu_type, "amd") || !strcmp(gpu_type, "ati")) {
            hu.proc_type = PROC_TYPE_AMD_GPU;
            hu.gpu_usage = gpu_usage;
        } else if (!strcmp(gpu_type, "nvidia")) {
            hu.proc_type = PROC_TYPE_NVIDIA_GPU;
            hu.gpu_usage = gpu_usage;
        } else if (strstr(gpu_type, "intel")==gpu_type) {
            hu.proc_type = PROC_TYPE_INTEL_GPU;
            hu.gpu_usage = gpu_usage;
        } else if (!strcmp(gpu_type, "miner_asic")) {
            hu.proc_type = PROC_TYPE_MINER_ASIC;
            hu.gpu_usage = gpu_usage;
        } else {
            if (config.debug_version_select) {
                log_messages.printf(MSG_NORMAL,
                    "[version] plan_class_spec: unknown GPU supplied: %s\n",
                    gpu_type
                );
            }
        }
    } else {
        // CPU only
        //
        if (avg_ncpus) {
            hu.avg_ncpus = avg_ncpus;
        } else {
            if (can_use_multicore) {
                if (max_threads > g_wreq->effective_ncpus) {
                    hu.avg_ncpus = g_wreq->effective_ncpus;
                } else {
                    hu.avg_ncpus = max_threads;
                }

                // if per-CPU mem usage given
                //
                if (mem_usage_per_cpu) {
                    if (!min_ncpus) min_ncpus = 1;
                    double mem_usage_seq = mem_usage_base + min_ncpus*mem_usage_per_cpu;

                    // see if client has enough memory to run at all
                    //
                    if (mem_usage_seq > g_wreq->usable_ram) {
                        if (config.debug_version_select) {
                            log_messages.printf(MSG_NORMAL,
                                "[version] plan_class_spec: insufficient multicore RAM; %f < %f",
                                g_wreq->usable_ram, mem_usage_seq
                            );
                        }
                        return false;
                    }

                    // see how many CPUs we could use given memory usage
                    //
                    int n = (g_wreq->usable_ram - mem_usage_base)/mem_usage_per_cpu;
                    // don't use more than this many
                    //
                    if (n < hu.avg_ncpus) {
                        hu.avg_ncpus = n;
                    }

                    // compute memory usage; overrides wu.rsc_memory_bound
                    //
                    hu.mem_usage = mem_usage_base + hu.avg_ncpus*mem_usage_per_cpu;
                    char buf[256];
                    sprintf(buf, " --memory_size_mb %.0f", hu.mem_usage/MEGA);
                    strcat(hu.cmdline, buf);
                }
            } else {
                hu.avg_ncpus = 1;
            }
        }
        if (nthreads_cmdline) {
            char buf[256];
            sprintf(buf, " --nthreads %d", (int)hu.avg_ncpus);
            strcat(hu.cmdline, buf);
        }

        hu.peak_flops = capped_host_fpops() * hu.avg_ncpus;
        hu.projected_flops = capped_host_fpops() * hu.avg_ncpus * projected_flops_scale;
    }
    hu.max_ncpus = hu.avg_ncpus;

#if 0
    if (config.debug_version_select) {
        log_messages.printf(MSG_NORMAL,
            "[version] plan_class_spec: host_flops: %e, \tscale: %.2f, \tprojected_flops: %e, \tpeak_flops: %e\n",
            sreq.host.p_fpops, projected_flops_scale, hu.projected_flops,
            hu.peak_flops
        );
    }
#endif

    return true;

}