static bool submit_work(struct thr_info *thr, const struct work *work_in) { struct workio_cmd *wc; /* fill out work request message */ wc = calloc(1, sizeof(*wc)); if (!wc) return false; wc->u.work = malloc(sizeof(*work_in)); if (!wc->u.work) goto err_out; wc->cmd = WC_SUBMIT_WORK; wc->thr = thr; memcpy(wc->u.work, work_in, sizeof(*work_in)); /* send solution to workio thread */ if (!tq_push(thr_info[work_thr_id].q, wc)) goto err_out; return true; err_out: workio_cmd_free(wc); return false; }
static bool get_work(struct thr_info *thr, struct work *work) { struct workio_cmd *wc; struct work *work_heap; /* fill out work request message */ wc = calloc(1, sizeof(*wc)); if (!wc) return false; wc->cmd = WC_GET_WORK; wc->thr = thr; /* send work request to workio thread */ if (!tq_push(thr_info[work_thr_id].q, wc)) { workio_cmd_free(wc); return false; } /* wait for response, a unit of work */ work_heap = tq_pop(thr->q, NULL); if (!work_heap) return false; /* copy returned work into storage provided by caller */ memcpy(work, work_heap, sizeof(*work)); free(work_heap); return true; }
static bool workio_get_work(struct workio_cmd *wc, CURL *curl) { struct work *ret_work; int failures = 0; ret_work = calloc(1, sizeof(*ret_work)); if (!ret_work) return false; /* obtain new work from bitcoin via JSON-RPC */ while (!get_upstream_work(curl, ret_work)) { if (unlikely((opt_retries >= 0) && (++failures > opt_retries))) { applog(LOG_ERR, "json_rpc_call failed, terminating workio thread"); free(ret_work); return false; } /* pause, then restart work-request loop */ applog(LOG_ERR, "json_rpc_call failed, retry after %d seconds", opt_fail_pause); sleep(opt_fail_pause); } /* send work to requesting thread */ if (!tq_push(wc->thr->q, ret_work)) free(ret_work); return true; }
/* In dynamic mode, only the first thread of each device will be in use. * This potentially could start a thread that was stopped with the start-stop * options if one were to disable dynamic from the menu on a paused GPU */ void pause_dynamic_threads(int gpu) { struct cgpu_info *cgpu = &gpus[gpu]; int i; for (i = 1; i < cgpu->threads; i++) { struct thr_info *thr = &thr_info[i]; if (!thr->pause && cgpu->dynamic) { applog(LOG_WARNING, "Disabling extra threads due to dynamic mode."); applog(LOG_WARNING, "Tune dynamic intensity with --gpu-dyninterval"); } thr->pause = cgpu->dynamic; if (!cgpu->dynamic && cgpu->deven != DEV_DISABLED) tq_push(thr->q, &ping); } }
// pool switching code bool pool_switch(int thr_id, int pooln) { int prevn = cur_pooln; bool algo_switch = false; struct pool_infos *prev = &pools[cur_pooln]; struct pool_infos* p = NULL; // save prev stratum connection infos (struct) if (prev->type & POOL_STRATUM) { // may not be the right moment to free, // to check if required on submit... stratum_free_job(&stratum); prev->stratum = stratum; } if (pooln < num_pools) { cur_pooln = pooln; p = &pools[cur_pooln]; } else { applog(LOG_ERR, "Switch to inexistant pool %d!", pooln); return false; } // save global attributes prev->allow_mininginfo = allow_mininginfo; prev->allow_gbt = allow_gbt; prev->check_dups = check_dups; pthread_mutex_lock(&stratum_work_lock); free(rpc_user); rpc_user = strdup(p->user); free(rpc_pass); rpc_pass = strdup(p->pass); free(rpc_url); rpc_url = strdup(p->url); short_url = p->short_url; // just a pointer, no alloc opt_scantime = p->scantime; opt_max_diff = p->max_diff; opt_max_rate = p->max_rate; opt_shares_limit = p->shares_limit; opt_time_limit = p->time_limit; want_stratum = have_stratum = (p->type & POOL_STRATUM) != 0; // yiimp stats reporting opt_stratum_stats = (strstr(p->pass, "stats") != NULL) || (strcmp(p->user, "benchmark") == 0); pthread_mutex_unlock(&stratum_work_lock); // algo "blind" switch without free, not proper // todo: barrier required to free algo resources if (p->algo != (int) opt_algo) { if (opt_algo != ALGO_AUTO) { algo_switch = true; pthread_mutex_lock(&stats_lock); for (int n=0; n<opt_n_threads; n++) thr_hashrates[n] = 0.; stats_purge_all(); if (check_dups) hashlog_purge_all(); pthread_mutex_unlock(&stats_lock); } opt_algo = (enum sha_algos) p->algo; } if (prevn != cur_pooln) { pool_switch_count++; net_diff = 0; g_work_time = 0; g_work.data[0] = 0; pool_is_switching = true; stratum_need_reset = true; // used to get the pool uptime firstwork_time = time(NULL); restart_threads(); // reset wait states for (int n=0; n<opt_n_threads; n++) conditional_state[n] = false; // restore flags allow_gbt = p->allow_gbt; allow_mininginfo = p->allow_mininginfo; check_dups = p->check_dups; if (want_stratum) { // temporary... until stratum code cleanup stratum = p->stratum; stratum.pooln = cur_pooln; // unlock the stratum thread tq_push(thr_info[stratum_thr_id].q, strdup(rpc_url)); applog(LOG_BLUE, "Switch to stratum pool %d: %s", cur_pooln, strlen(p->name) ? p->name : p->short_url); } else { applog(LOG_BLUE, "Switch to pool %d: %s", cur_pooln, strlen(p->name) ? p->name : p->short_url); } // will unlock the longpoll thread on /LP url receive want_longpoll = (p->type & POOL_LONGPOLL) || !(p->type & POOL_STRATUM); if (want_longpoll) { pthread_mutex_lock(&stratum_work_lock); // will issue a lp_url request to unlock the longpoll thread have_longpoll = false; get_work(&thr_info[0], &g_work); pthread_mutex_unlock(&stratum_work_lock); } } return true; }
void manage_gpu(void) { struct thr_info *thr; int selected, gpu, i; char checkin[40]; char input; if (!opt_g_threads) return; opt_loginput = true; immedok(logwin, true); clear_logwin(); retry: for (gpu = 0; gpu < nDevs; gpu++) { struct cgpu_info *cgpu = &gpus[gpu]; double displayed_rolling, displayed_total; bool mhash_base = true; displayed_rolling = cgpu->rolling; displayed_total = cgpu->total_mhashes / total_secs; if (displayed_rolling < 1) { displayed_rolling *= 1000; displayed_total *= 1000; mhash_base = false; } wlog("GPU %d: %.1f / %.1f %sh/s | A:%d R:%d HW:%d U:%.2f/m I:%d\n", gpu, displayed_rolling, displayed_total, mhash_base ? "M" : "K", cgpu->accepted, cgpu->rejected, cgpu->hw_errors, cgpu->utility, cgpu->intensity); #ifdef HAVE_ADL if (gpus[gpu].has_adl) { int engineclock = 0, memclock = 0, activity = 0, fanspeed = 0, fanpercent = 0, powertune = 0; float temp = 0, vddc = 0; if (gpu_stats(gpu, &temp, &engineclock, &memclock, &vddc, &activity, &fanspeed, &fanpercent, &powertune)) { char logline[255]; strcpy(logline, ""); // In case it has no data if (temp != -1) sprintf(logline, "%.1f C ", temp); if (fanspeed != -1 || fanpercent != -1) { tailsprintf(logline, "F: "); if (fanpercent != -1) tailsprintf(logline, "%d%% ", fanpercent); if (fanspeed != -1) tailsprintf(logline, "(%d RPM) ", fanspeed); tailsprintf(logline, " "); } if (engineclock != -1) tailsprintf(logline, "E: %d MHz ", engineclock); if (memclock != -1) tailsprintf(logline, "M: %d Mhz ", memclock); if (vddc != -1) tailsprintf(logline, "V: %.3fV ", vddc); if (activity != -1) tailsprintf(logline, "A: %d%% ", activity); if (powertune != -1) tailsprintf(logline, "P: %d%%", powertune); tailsprintf(logline, "\n"); wlog(logline); } } #endif wlog("Last initialised: %s\n", cgpu->init); wlog("Intensity: "); if (gpus[gpu].dynamic) wlog("Dynamic (only one thread in use)\n"); else wlog("%d\n", gpus[gpu].intensity); for (i = 0; i < mining_threads; i++) { thr = &thr_info[i]; if (thr->cgpu != cgpu) continue; get_datestamp(checkin, &thr->last); displayed_rolling = thr->rolling; if (!mhash_base) displayed_rolling *= 1000; wlog("Thread %d: %.1f %sh/s %s ", i, displayed_rolling, mhash_base ? "M" : "K" , cgpu->deven != DEV_DISABLED ? "Enabled" : "Disabled"); switch (cgpu->status) { default: case LIFE_WELL: wlog("ALIVE"); break; case LIFE_SICK: wlog("SICK reported in %s", checkin); break; case LIFE_DEAD: wlog("DEAD reported in %s", checkin); break; case LIFE_INIT: case LIFE_NOSTART: wlog("Never started"); break; } if (thr->pause) wlog(" paused"); wlog("\n"); } wlog("\n"); } wlogprint("[E]nable [D]isable [I]ntensity [R]estart GPU %s\n",adl_active ? "[C]hange settings" : ""); wlogprint("Or press any other key to continue\n"); input = getch(); if (nDevs == 1) selected = 0; else selected = -1; if (!strncasecmp(&input, "e", 1)) { struct cgpu_info *cgpu; if (selected) selected = curses_int("Select GPU to enable"); if (selected < 0 || selected >= nDevs) { wlogprint("Invalid selection\n"); goto retry; } if (gpus[selected].deven != DEV_DISABLED) { wlogprint("Device already enabled\n"); goto retry; } gpus[selected].deven = DEV_ENABLED; for (i = 0; i < mining_threads; ++i) { thr = &thr_info[i]; cgpu = thr->cgpu; if (cgpu->api != &opencl_api) continue; if (dev_from_id(i) != selected) continue; if (cgpu->status != LIFE_WELL) { wlogprint("Must restart device before enabling it"); goto retry; } applog(LOG_DEBUG, "Pushing ping to thread %d", thr->id); tq_push(thr->q, &ping); } goto retry; } if (!strncasecmp(&input, "d", 1)) { if (selected) selected = curses_int("Select GPU to disable"); if (selected < 0 || selected >= nDevs) { wlogprint("Invalid selection\n"); goto retry; } if (gpus[selected].deven == DEV_DISABLED) { wlogprint("Device already disabled\n"); goto retry; } gpus[selected].deven = DEV_DISABLED; goto retry; } else if (!strncasecmp(&input, "i", 1)) { int intensity; char *intvar; if (selected) selected = curses_int("Select GPU to change intensity on"); if (selected < 0 || selected >= nDevs) { wlogprint("Invalid selection\n"); goto retry; } intvar = curses_input("Set GPU scan intensity (d or " _MIN_INTENSITY_STR " -> " _MAX_INTENSITY_STR ")"); if (!intvar) { wlogprint("Invalid input\n"); goto retry; } if (!strncasecmp(intvar, "d", 1)) { wlogprint("Dynamic mode enabled on gpu %d\n", selected); gpus[selected].dynamic = true; pause_dynamic_threads(selected); free(intvar); goto retry; } intensity = atoi(intvar); free(intvar); if (intensity < MIN_INTENSITY || intensity > MAX_INTENSITY) { wlogprint("Invalid selection\n"); goto retry; } gpus[selected].dynamic = false; gpus[selected].intensity = intensity; wlogprint("Intensity on gpu %d set to %d\n", selected, intensity); pause_dynamic_threads(selected); goto retry; } else if (!strncasecmp(&input, "r", 1)) { if (selected) selected = curses_int("Select GPU to attempt to restart"); if (selected < 0 || selected >= nDevs) { wlogprint("Invalid selection\n"); goto retry; } wlogprint("Attempting to restart threads of GPU %d\n", selected); reinit_device(&gpus[selected]); goto retry; } else if (adl_active && (!strncasecmp(&input, "c", 1))) { if (selected) selected = curses_int("Select GPU to change settings on"); if (selected < 0 || selected >= nDevs) { wlogprint("Invalid selection\n"); goto retry; } change_gpusettings(selected); goto retry; } else clear_logwin(); immedok(logwin, false); opt_loginput = false; }
static void reinit_opencl_device(struct cgpu_info *gpu) { tq_push(thr_info[gpur_thr_id].q, gpu); }
/* We have only one thread that ever re-initialises GPUs, thus if any GPU * init command fails due to a completely wedged GPU, the thread will never * return, unable to harm other GPUs. If it does return, it means we only had * a soft failure and then the reinit_gpu thread is ready to tackle another * GPU */ void *reinit_gpu(void *userdata) { struct thr_info *mythr = userdata; struct cgpu_info *cgpu; struct thr_info *thr; struct timeval now; char name[256]; int thr_id; int gpu; pthread_detach(pthread_self()); select_cgpu: cgpu = tq_pop(mythr->q, NULL); if (!cgpu) goto out; if (clDevicesNum() != nDevs) { applog(LOG_WARNING, "Hardware not reporting same number of active devices, will not attempt to restart GPU"); goto out; } gpu = cgpu->device_id; for (thr_id = 0; thr_id < mining_threads; ++thr_id) { thr = &thr_info[thr_id]; cgpu = thr->cgpu; if (cgpu->api != &opencl_api) continue; if (dev_from_id(thr_id) != gpu) continue; thr = &thr_info[thr_id]; if (!thr) { applog(LOG_WARNING, "No reference to thread %d exists", thr_id); continue; } thr->rolling = thr->cgpu->rolling = 0; /* Reports the last time we tried to revive a sick GPU */ gettimeofday(&thr->sick, NULL); if (!pthread_cancel(thr->pth)) { applog(LOG_WARNING, "Thread %d still exists, killing it off", thr_id); } else applog(LOG_WARNING, "Thread %d no longer exists", thr_id); } for (thr_id = 0; thr_id < mining_threads; ++thr_id) { int virtual_gpu; thr = &thr_info[thr_id]; cgpu = thr->cgpu; if (cgpu->api != &opencl_api) continue; if (dev_from_id(thr_id) != gpu) continue; virtual_gpu = cgpu->virtual_gpu; /* Lose this ram cause we may get stuck here! */ //tq_freeze(thr->q); thr->q = tq_new(); if (!thr->q) quit(1, "Failed to tq_new in reinit_gpu"); /* Lose this ram cause we may dereference in the dying thread! */ //free(clState); applog(LOG_INFO, "Reinit GPU thread %d", thr_id); clStates[thr_id] = initCl(virtual_gpu, name, sizeof(name)); if (!clStates[thr_id]) { applog(LOG_ERR, "Failed to reinit GPU thread %d", thr_id); goto select_cgpu; } applog(LOG_INFO, "initCl() finished. Found %s", name); if (unlikely(thr_info_create(thr, NULL, miner_thread, thr))) { applog(LOG_ERR, "thread %d create failed", thr_id); return NULL; } applog(LOG_WARNING, "Thread %d restarted", thr_id); } gettimeofday(&now, NULL); get_datestamp(cgpu->init, &now); for (thr_id = 0; thr_id < mining_threads; ++thr_id) { thr = &thr_info[thr_id]; cgpu = thr->cgpu; if (cgpu->api != &opencl_api) continue; if (dev_from_id(thr_id) != gpu) continue; tq_push(thr->q, &ping); } goto select_cgpu; out: return NULL; }
json_t *json_rpc_call(CURL *curl, const char *url, const char *userpass, const char *rpc_req, bool longpoll_scan, bool longpoll, int *curl_err) { json_t *val, *err_val, *res_val; int rc; struct data_buffer all_data = {0}; struct upload_buffer upload_data; json_error_t err; struct curl_slist *headers = NULL; char len_hdr[64]; char curl_err_str[CURL_ERROR_SIZE]; long timeout = longpoll ? opt_timeout : 30; struct header_info hi = {0}; bool lp_scanning = longpoll_scan && !have_longpoll; /* it is assumed that 'curl' is freshly [re]initialized at this pt */ if (opt_protocol) curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); curl_easy_setopt(curl, CURLOPT_URL, url); if (opt_cert) curl_easy_setopt(curl, CURLOPT_CAINFO, opt_cert); curl_easy_setopt(curl, CURLOPT_ENCODING, ""); curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1); curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); curl_easy_setopt(curl, CURLOPT_TCP_NODELAY, 1); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, all_data_cb); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &all_data); curl_easy_setopt(curl, CURLOPT_READFUNCTION, upload_data_cb); curl_easy_setopt(curl, CURLOPT_READDATA, &upload_data); #if LIBCURL_VERSION_NUM >= 0x071200 curl_easy_setopt(curl, CURLOPT_SEEKFUNCTION, &seek_data_cb); curl_easy_setopt(curl, CURLOPT_SEEKDATA, &upload_data); #endif curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_err_str); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout); curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, resp_hdr_cb); curl_easy_setopt(curl, CURLOPT_HEADERDATA, &hi); if (opt_proxy) { curl_easy_setopt(curl, CURLOPT_PROXY, opt_proxy); curl_easy_setopt(curl, CURLOPT_PROXYTYPE, opt_proxy_type); } if (userpass) { curl_easy_setopt(curl, CURLOPT_USERPWD, userpass); curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); } #if LIBCURL_VERSION_NUM >= 0x070f06 if (longpoll) curl_easy_setopt(curl, CURLOPT_SOCKOPTFUNCTION, sockopt_keepalive_cb); #endif curl_easy_setopt(curl, CURLOPT_POST, 1); if (opt_protocol) applog(LOG_DEBUG, "JSON protocol request:\n%s\n", rpc_req); upload_data.buf = rpc_req; upload_data.len = strlen(rpc_req); upload_data.pos = 0; sprintf(len_hdr, "Content-Length: %lu", (unsigned long) upload_data.len); headers = curl_slist_append(headers, "Content-Type: application/json"); headers = curl_slist_append(headers, len_hdr); headers = curl_slist_append(headers, "User-Agent: " USER_AGENT); headers = curl_slist_append(headers, "X-Mining-Extensions: midstate"); headers = curl_slist_append(headers, "Accept:"); /* disable Accept hdr*/ headers = curl_slist_append(headers, "Expect:"); /* disable Expect hdr*/ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); rc = curl_easy_perform(curl); if (curl_err != NULL) *curl_err = rc; if (rc) { if (!(longpoll && rc == CURLE_OPERATION_TIMEDOUT)) applog(LOG_ERR, "HTTP request failed: %s", curl_err_str); goto err_out; } /* If X-Stratum was found, activate Stratum */ if (want_stratum && hi.stratum_url && !strncasecmp(hi.stratum_url, "stratum+tcp://", 14) && !(opt_proxy && opt_proxy_type == CURLPROXY_HTTP)) { have_stratum = true; tq_push(thr_info[stratum_thr_id].q, hi.stratum_url); hi.stratum_url = NULL; } /* If X-Long-Polling was found, activate long polling */ if (lp_scanning && hi.lp_path && !have_stratum) { have_longpoll = true; tq_push(thr_info[longpoll_thr_id].q, hi.lp_path); hi.lp_path = NULL; } if (!all_data.buf) { applog(LOG_ERR, "Empty data received in json_rpc_call."); goto err_out; } val = JSON_LOADS((const char*)all_data.buf, &err); if (!val) { applog(LOG_ERR, "JSON decode failed(%d): %s", err.line, err.text); goto err_out; } if (opt_protocol) { char *s = json_dumps(val, JSON_INDENT(3)); applog(LOG_DEBUG, "JSON protocol response:\n%s", s); free(s); } /* JSON-RPC valid response returns a non-null 'result', * and a null 'error'. */ res_val = json_object_get(val, "result"); err_val = json_object_get(val, "error"); if (!res_val || json_is_null(res_val) || (err_val && !json_is_null(err_val))) { char *s; if (err_val) s = json_dumps(err_val, JSON_INDENT(3)); else s = strdup("(unknown reason)"); applog(LOG_ERR, "JSON-RPC call failed: %s", s); free(s); goto err_out; } if (hi.reason) json_object_set_new(val, "reject-reason", json_string(hi.reason)); databuf_free(&all_data); curl_slist_free_all(headers); curl_easy_reset(curl); return val; err_out: free(hi.lp_path); free(hi.reason); free(hi.stratum_url); databuf_free(&all_data); curl_slist_free_all(headers); curl_easy_reset(curl); return NULL; }
static void reinit_cpu_device(struct cgpu_info *cpu) { tq_push(thr_info[cpur_thr_id].q, cpu); }