Professional Documents
Culture Documents
#include <float.h>
#include <limits.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <strings.h>
#include <sys/time.h>
#include <unistd.h>
#include <math.h>
#include "config.h"
#include "compat.h"
#include "miner.h"
#include "usbutils.h"
#define K1 "K1"
#define K16 "K16"
#define K64 "K64"
#define MIDSTATE_BYTES 32
#define MERKLE_OFFSET 64
#define MERKLE_BYTES 12
/*
* Work older than 5s will already be completed
* FYI it must not be possible to complete 256 work
* items this quickly on a single device -
* thus limited to 219.9GH/s per device
*/
#define OLD_WORK_MS ((int)(5 * 1000))
/*
* How many incorrect slave counts to ignore in a row
* 2 means it allows random grabage returned twice
* Until slaves are implemented, this should never occur
* so allowing 2 in a row should ignore random errros
*/
#define KLN_ISS_IGNORE 2
/*
* If the queue status hasn't been updated for this long then do it now
* 5GH/s = 859ms per full nonce range
*/
#define LATE_UPDATE_MS ((int)(2.5 * 1000))
struct klondike_info {
pthread_rwlock_t stat_lock;
struct thr_info replies_thr;
cglock_t klist_lock;
KLIST *used;
KLIST *free;
int kline_count;
int used_count;
int block_seq;
KLIST *status;
DEVINFO *devinfo;
KLIST *cfg;
JOBQUE *jobque;
int noncecount;
uint64_t hashcount;
uint64_t errorcount;
uint64_t noisecount;
int incorrect_slave_sequential;
int wque_size;
int wque_cleared;
bool initialised;
};
klninfo->kline_count += MAX_KLINES;
klist[0].prev = NULL;
klist[0].next = &(klist[1]);
for (i = 1; i < MAX_KLINES-1; i++) {
klist[i].prev = &klist[i-1];
klist[i].next = &klist[i+1];
}
klist[MAX_KLINES-1].prev = &(klist[MAX_KLINES-2]);
klist[MAX_KLINES-1].next = NULL;
return klist;
}
cg_wlock(&klninfo->klist_lock);
if (klninfo->free == NULL) {
ran_out = klninfo->kline_count;
klninfo->free = new_klist_set(klncgpu);
snprintf(errbuf, sizeof(errbuf),
"%s%i: KLINE count exceeded %d, now %d",
klncgpu->drv->name, klncgpu->device_id,
ran_out, klninfo->kline_count);
}
kitem = klninfo->free;
klninfo->free = klninfo->free->next;
if (klninfo->free)
klninfo->free->prev = NULL;
kitem->next = klninfo->used;
kitem->prev = NULL;
if (kitem->next)
kitem->next->prev = kitem;
klninfo->used = kitem;
kitem->ready = false;
kitem->working = false;
klninfo->used_count++;
cg_wunlock(&klninfo->klist_lock);
if (ran_out > 0)
applog(LOG_WARNING, "%s", errbuf);
return kitem;
}
cg_wlock(&klninfo->klist_lock);
if (kitem == klninfo->used)
klninfo->used = kitem->next;
if (kitem->next)
kitem->next->prev = kitem->prev;
if (kitem->prev)
kitem->prev->next = kitem->next;
kitem->next = klninfo->free;
if (klninfo->free)
klninfo->free->prev = kitem;
kitem->prev = NULL;
klninfo->free = kitem;
klninfo->used_count--;
cg_wunlock(&klninfo->klist_lock);
return NULL;
}
static double cvtKlnToC(uint8_t temp)
{
double Rt, stein, celsius;
if (temp == 0)
return 0.0;
return celsius;
}
if (Rt == -1000.0)
Rt++;
return (int)temp;
}
static void display_kline(struct cgpu_info *klncgpu, KLINE *kline, const char *msg)
{
char *hexdata;
switch (kline->hd.cmd) {
case KLN_CMD_NONCE:
applog(READ_DEBUG,
"%s%i:%d %s work [%c] dev=%d workid=%d"
" nonce=0x%08x",
klncgpu->drv->name, klncgpu->device_id,
(int)(kline->wr.dev), msg, kline->wr.cmd,
(int)(kline->wr.dev),
(int)(kline->wr.workid),
(unsigned int)K_NONCE(kline->wr.nonce) - 0xC0);
break;
case KLN_CMD_STATUS:
case KLN_CMD_WORK:
case KLN_CMD_ENABLE:
case KLN_CMD_ABORT:
applog(READ_DEBUG,
"%s%i:%d %s status [%c] dev=%d chips=%d"
" slaves=%d workcq=%d workid=%d temp=%d fan=%d"
" errors=%d hashes=%d max=%d noise=%d",
klncgpu->drv->name, klncgpu->device_id,
(int)(kline->ws.dev), msg, kline->ws.cmd,
(int)(kline->ws.dev),
(int)(kline->ws.chipcount),
(int)(kline->ws.slavecount),
(int)(kline->ws.workqc),
(int)(kline->ws.workid),
(int)(kline->ws.temp),
(int)(kline->ws.fanspeed),
(int)(kline->ws.errorcount),
K_HASHCOUNT(kline->ws.hashcount),
K_MAXCOUNT(kline->ws.maxcount),
(int)(kline->ws.noise));
break;
case KLN_CMD_CONFIG:
applog(READ_DEBUG,
"%s%i:%d %s config [%c] dev=%d clock=%d"
" temptarget=%d tempcrit=%d fan=%d",
klncgpu->drv->name, klncgpu->device_id,
(int)(kline->cfg.dev), msg, kline->cfg.cmd,
(int)(kline->cfg.dev),
K_HASHCLOCK(kline->cfg.hashclock),
(int)(kline->cfg.temptarget),
(int)(kline->cfg.tempcritical),
(int)(kline->cfg.fantarget));
break;
case KLN_CMD_IDENT:
applog(READ_DEBUG,
"%s%i:%d %s info [%c] version=0x%02x prod=%.7s"
" serial=0x%08x",
klncgpu->drv->name, klncgpu->device_id,
(int)(kline->hd.dev), msg, kline->hd.cmd,
(int)(kline->id.version),
kline->id.product,
(unsigned int)K_SERIAL(kline->id.serial));
break;
default:
hexdata = bin2hex((unsigned char *)&(kline->hd.dev), REPLY_SIZE -
1);
applog(LOG_ERR,
"%s%i:%d %s [%c:%s] unknown and ignored",
klncgpu->drv->name, klncgpu->device_id,
(int)(kline->hd.dev), msg, kline->hd.cmd,
hexdata);
free(hexdata);
break;
}
}
switch (kline->hd.cmd) {
case KLN_CMD_WORK:
applog(READ_DEBUG,
"%s%i:%d %s work [%c] dev=%d workid=0x%02x ...",
klncgpu->drv->name, klncgpu->device_id,
(int)(kline->wt.dev), msg, kline->ws.cmd,
(int)(kline->wt.dev),
(int)(kline->wt.workid));
break;
case KLN_CMD_CONFIG:
applog(READ_DEBUG,
"%s%i:%d %s config [%c] dev=%d clock=%d"
" temptarget=%d tempcrit=%d fan=%d",
klncgpu->drv->name, klncgpu->device_id,
(int)(kline->cfg.dev), msg, kline->cfg.cmd,
(int)(kline->cfg.dev),
K_HASHCLOCK(kline->cfg.hashclock),
(int)(kline->cfg.temptarget),
(int)(kline->cfg.tempcritical),
(int)(kline->cfg.fantarget));
break;
case KLN_CMD_IDENT:
case KLN_CMD_STATUS:
case KLN_CMD_ABORT:
applog(READ_DEBUG,
"%s%i:%d %s cmd [%c]",
klncgpu->drv->name, klncgpu->device_id,
(int)(kline->hd.dev), msg, kline->hd.cmd);
break;
case KLN_CMD_ENABLE:
applog(READ_DEBUG,
"%s%i:%d %s enable [%c] enable=%c",
klncgpu->drv->name, klncgpu->device_id,
(int)(kline->hd.dev), msg, kline->hd.cmd,
(char)(kline->hd.buf[0]));
break;
case KLN_CMD_NONCE:
default:
hexdata = bin2hex((unsigned char *)&(kline->hd.dev), REPLY_SIZE -
1);
applog(LOG_ERR,
"%s%i:%d %s [%c:%s] unknown/unexpected and ignored",
klncgpu->drv->name, klncgpu->device_id,
(int)(kline->hd.dev), msg, kline->hd.cmd,
hexdata);
free(hexdata);
break;
}
}
if (klncgpu->usbinfo.nodev)
return false;
return true;
}
rd_lock(&(klninfo->stat_lock));
slaves = klninfo->status[0].kline.ws.slavecount;
rd_unlock(&(klninfo->stat_lock));
zero_kline(&kline);
kline.hd.cmd = KLN_CMD_ENABLE;
kline.hd.dev = 0;
kline.hd.buf[0] = KLN_CMD_ENABLE_ON;
if (ok)
cgsleep_ms(50);
return ok;
}
zero_kline(&kline);
kline.hd.cmd = KLN_CMD_ENABLE;
kline.hd.buf[0] = KLN_CMD_ENABLE_OFF;
for (i = (all ? 0 : dev); i <= dev; i++) {
kline.hd.dev = i;
SendCmd(klncgpu, &kline, KSENDHD(1));
}
}
klninfo->initialised = false;
zero_kline(&kline);
kline.hd.cmd = KLN_CMD_STATUS;
kline.hd.dev = 0;
kitem = SendCmdGetReply(klncgpu, &kline, 0);
if (kitem == NULL)
return false;
slaves = kitem->kline.ws.slavecount;
if (klninfo->status == NULL) {
applog(LOG_DEBUG, "%s%i: initializing data",
klncgpu->drv->name, klncgpu->device_id);
// alloc space for status, devinfo, cfg and jobque for master and
slaves
klninfo->status = calloc(slaves+1, sizeof(*(klninfo->status)));
if (unlikely(!klninfo->status))
quit(1, "Failed to calloc status array in klondke_get_stats");
klninfo->devinfo = calloc(slaves+1, sizeof(*(klninfo->devinfo)));
if (unlikely(!klninfo->devinfo))
quit(1, "Failed to calloc devinfo array in klondke_get_stats");
klninfo->cfg = calloc(slaves+1, sizeof(*(klninfo->cfg)));
if (unlikely(!klninfo->cfg))
quit(1, "Failed to calloc cfg array in klondke_get_stats");
klninfo->jobque = calloc(slaves+1, sizeof(*(klninfo->jobque)));
if (unlikely(!klninfo->jobque))
quit(1, "Failed to calloc jobque array in klondke_get_stats");
}
int size = 2;
bool ok = kln_enable(klncgpu);
if (!ok)
applog(LOG_ERR, "%s%i: failed to enable", klncgpu->drv->name, klncgpu-
>device_id);
return ok;
}
static void control_init(struct cgpu_info *klncgpu)
{
int err, interface;
if (klncgpu->usbinfo.nodev)
return;
interface = usb_interface(klncgpu);
if (unlikely(!klncgpu))
quit(1, "Failed to calloc klncgpu in klondike_detect_one");
klninfo->free = new_klist_set(klncgpu);
control_init(klncgpu);
zero_kline(&kline);
kline.hd.cmd = KLN_CMD_IDENT;
kline.hd.dev = 0;
SendCmdGetReply(klncgpu, &kline, KSENDHD(0));
*/
}
work = NULL;
cgtime(&tv_now);
rd_lock(&(klncgpu->qlock));
HASH_ITER(hh, klncgpu->queued_work, look, tmp) {
if (ms_tdiff(&tv_now, &(look->tv_stamp)) < OLD_WORK_MS &&
(look->subid == (kline->wr.dev*256 + kline->wr.workid))) {
work = look;
break;
}
}
rd_unlock(&(klncgpu->qlock));
if (work) {
wr_lock(&(klninfo->stat_lock));
klninfo->devinfo[kline->wr.dev].noncecount++;
klninfo->noncecount++;
wr_unlock(&(klninfo->stat_lock));
cgtime(&tv_now);
bool ok = submit_nonce(klncgpu->thr[0], work, nonce);
klninfo->devinfo[kline->wr.dev].chipstats[(nonce / klninfo-
>devinfo[kline->wr.dev].rangesize) + (ok ? 0 : klninfo->status[kline-
>wr.dev].kline.ws.chipcount)]++;
if (klninfo->nonce_count > 0) {
us_diff = us_tdiff(&(kitem->tv_when), &(klninfo-
>tv_last_nonce_received));
if (klninfo->nonce_count == 1) {
klninfo->nonce_min = us_diff;
klninfo->nonce_max = us_diff;
} else {
if (klninfo->nonce_min > us_diff)
klninfo->nonce_min = us_diff;
if (klninfo->nonce_max < us_diff)
klninfo->nonce_max = us_diff;
}
klninfo->nonce_total += us_diff;
}
klninfo->nonce_count++;
memcpy(&(klninfo->tv_last_nonce_received), &(kitem->tv_when),
sizeof(klninfo->tv_last_nonce_received));
return;
}
//inc_hw_errors(klncgpu->thr[0]);
}
if (kitem == NULL)
kitem = allocate_kitem(klncgpu);
else
memset((void *)&(kitem->kline), 0, sizeof(kitem->kline));
switch (kitem->kline.hd.cmd) {
case KLN_CMD_NONCE:
klondike_check_nonce(klncgpu, kitem);
display_kline(klncgpu, &kitem->kline, msg_reply);
break;
case KLN_CMD_WORK:
// We can't do/check this until it's initialised
if (klninfo->initialised) {
dev = kitem->kline.ws.dev;
if (kitem->kline.ws.workqc == 0) {
bool idle = false;
rd_lock(&(klninfo->stat_lock));
if (klninfo->jobque[dev].flushed ==
false)
idle = true;
slaves = klninfo-
>status[0].kline.ws.slavecount;
rd_unlock(&(klninfo->stat_lock));
if (idle)
applog(LOG_WARNING, "%s%i:%d went
idle before work was sent",
klncgpu->drv->name,
klncgpu->device_id,
dev);
}
wr_lock(&(klninfo->stat_lock));
klninfo->jobque[dev].flushed = false;
wr_unlock(&(klninfo->stat_lock));
}
case KLN_CMD_STATUS:
case KLN_CMD_ABORT:
// We can't do/check this until it's initialised
if (klninfo->initialised) {
isc = 0;
dev = kitem->kline.ws.dev;
wr_lock(&(klninfo->stat_lock));
klninfo->jobque[dev].workqc = (int)(kitem-
>kline.ws.workqc);
cgtime(&(klninfo->jobque[dev].last_update));
slaves = klninfo-
>status[0].kline.ws.slavecount;
overheat = klninfo->jobque[dev].overheat;
if (dev == 0) {
if (kitem->kline.ws.slavecount != slaves)
isc = ++klninfo-
>incorrect_slave_sequential;
else
isc = klninfo-
>incorrect_slave_sequential = 0;
}
wr_unlock(&(klninfo->stat_lock));
if (isc) {
applog(LOG_ERR, "%s%i:%d reply [%c] has a
diff"
" # of slaves=%d (curr=%d)
%s",
klncgpu->drv->name,
klncgpu->device_id,
dev,
(char)(kitem->kline.ws.cmd),
(int)(kitem-
>kline.ws.slavecount),
slaves,
isc <= KLN_ISS_IGNORE ? "" :
" disabling device");
if (isc > KLN_ISS_IGNORE)
usb_nodev(klncgpu);
break;
}
if (!overheat) {
double temp = cvtKlnToC(kitem-
>kline.ws.temp);
if (temp >= KLN_KILLWORK_TEMP) {
KLINE kline;
wr_lock(&(klninfo->stat_lock));
klninfo->jobque[dev].overheat =
true;
wr_unlock(&(klninfo->stat_lock));
applog(LOG_WARNING, "%s%i:%d
Critical overheat (%.0fC)",
klncgpu->drv->name,
klncgpu->device_id,
dev, temp);
zero_kline(&kline);
kline.hd.cmd = KLN_CMD_ABORT;
kline.hd.dev = dev;
sent = SendCmd(klncgpu, &kline,
KSENDHD(0));
kln_disable(klncgpu, dev, false);
if (!sent) {
applog(LOG_ERR, "%s%i:%d
overheat failed to"
" abort work -
disabling device",
klncgpu->drv-
>name,
klncgpu-
>device_id,
dev);
usb_nodev(klncgpu);
}
}
}
}
case KLN_CMD_ENABLE:
wr_lock(&(klninfo->stat_lock));
klninfo->errorcount += kitem->kline.ws.errorcount;
klninfo->noisecount += kitem->kline.ws.noise;
wr_unlock(&(klninfo->stat_lock));
display_kline(klncgpu, &kitem->kline, msg_reply);
kitem->ready = true;
kitem = NULL;
break;
case KLN_CMD_CONFIG:
display_kline(klncgpu, &kitem->kline, msg_reply);
kitem->ready = true;
kitem = NULL;
break;
case KLN_CMD_IDENT:
display_kline(klncgpu, &kitem->kline, msg_reply);
kitem->ready = true;
kitem = NULL;
break;
default:
display_kline(klncgpu, &kitem->kline, msg_reply);
break;
}
}
}
return NULL;
}
if (klninfo->initialised) {
wr_lock(&(klninfo->stat_lock));
klninfo->block_seq++;
slaves = klninfo->status[0].kline.ws.slavecount;
wr_unlock(&(klninfo->stat_lock));
return klondike_init(klncgpu);
}
if (klncgpu->usbinfo.nodev)
return false;
klondike_flush_work(klncgpu);
return true;
}
klncgpu->shutdown = true;
}
if (klncgpu->usbinfo.nodev)
return;
/*
KLINE kline;
zero_kline(&kline);
kline.hd.cmd = KLN_CMD_ENABLE;
kline.hd.dev = dev;
kline.hd.buf[0] = KLN_CMD_ENABLE_OFF;
kitem = SendCmdGetReply(klncgpu, &kline, KSENDHD(1));
*/
if (klncgpu->usbinfo.nodev)
return false;
zero_kline(&kline);
kline.wt.cmd = KLN_CMD_WORK;
kline.wt.dev = dev;
memcpy(kline.wt.midstate, work->midstate, MIDSTATE_BYTES);
memcpy(kline.wt.merkle, work->data + MERKLE_OFFSET, MERKLE_BYTES);
kline.wt.workid = (uint8_t)(klninfo->devinfo[dev].nextworkid++ & 0xFF);
work->subid = dev*256 + kline.wt.workid;
cgtime(&work->tv_stamp);
wr_lock(&(klninfo->stat_lock));
klninfo->wque_size = wque_size;
klninfo->wque_cleared = wque_cleared;
wr_unlock(&(klninfo->stat_lock));
return true;
}
return false;
}
if (klncgpu->shutdown == true)
return true;
cgtime(&now);
rd_lock(&(klninfo->stat_lock));
slaves = klninfo->status[0].kline.ws.slavecount;
for (dev = 0; dev <= slaves; dev++)
if (ms_tdiff(&now, &(klninfo->jobque[dev].last_update)) >
LATE_UPDATE_MS) {
klninfo->jobque[dev].late_update_count++;
seq = ++klninfo->jobque[dev].late_update_sequential;
rd_unlock(&(klninfo->stat_lock));
if (seq < LATE_UPDATE_LIMIT) {
applog(LOG_DEBUG, "%s%i:%d late update",
klncgpu->drv->name, klncgpu->device_id, dev);
klondike_get_stats(klncgpu);
goto que;
} else {
applog(LOG_WARNING, "%s%i:%d late update (%d) reached -
attempting reset",
klncgpu->drv->name, klncgpu->device_id,
dev, LATE_UPDATE_LIMIT);
control_init(klncgpu);
kln_enable(klncgpu);
klondike_get_stats(klncgpu);
rd_lock(&(klninfo->stat_lock));
howlong = ms_tdiff(&now, &(klninfo-
>jobque[dev].last_update));
if (howlong > LATE_UPDATE_MS) {
rd_unlock(&(klninfo->stat_lock));
if (howlong > LATE_UPDATE_NODEV_MS) {
applog(LOG_ERR, "%s%i:%d reset failed -
dropping device",
klncgpu->drv->name, klncgpu-
>device_id, dev);
usb_nodev(klncgpu);
} else
cgsleep_ms(LATE_UPDATE_SLEEP_MS);
return true;
}
break;
}
}
rd_unlock(&(klninfo->stat_lock));
que:
nowork = true;
for (queued = 0; queued < MAX_WORK_COUNT-1; queued++)
for (dev = 0; dev <= slaves; dev++) {
tryagain:
rd_lock(&(klninfo->stat_lock));
if (klninfo->jobque[dev].overheat) {
double temp = cvtKlnToC(klninfo->status[0].kline.ws.temp);
if ((queued == MAX_WORK_COUNT-2) &&
ms_tdiff(&now, &(klninfo->jobque[dev].last_update)) >
(LATE_UPDATE_MS/2)) {
rd_unlock(&(klninfo->stat_lock));
klondike_get_stats(klncgpu);
goto tryagain;
}
if (temp <= KLN_COOLED_DOWN) {
klninfo->jobque[dev].overheat = false;
rd_unlock(&(klninfo->stat_lock));
applog(LOG_WARNING, "%s%i:%d Overheat recovered
(%.0fC)",
klncgpu->drv->name, klncgpu-
>device_id,
dev, temp);
kln_enable(klncgpu);
goto tryagain;
} else {
rd_unlock(&(klninfo->stat_lock));
continue;
}
}
if (nowork)
cgsleep_ms(10); // avoid a hard loop in case we have nothing to do
return true;
}
if (klncgpu->usbinfo.nodev)
return -1;
restart_wait(thr, 200);
if (klninfo->status != NULL) {
rd_lock(&(klninfo->stat_lock));
slaves = klninfo->status[0].kline.ws.slavecount;
for (dev = 0; dev <= slaves; dev++) {
uint64_t newhashdev = 0, hashcount;
int maxcount;
hashcount = K_HASHCOUNT(klninfo->status[dev].kline.ws.hashcount);
maxcount = K_MAXCOUNT(klninfo->status[dev].kline.ws.maxcount);
// todo: chg this to check workid for wrapped instead
if (klninfo->devinfo[dev].lasthashcount > hashcount)
newhashdev += maxcount; // hash counter wrapped
newhashdev += hashcount - klninfo->devinfo[dev].lasthashcount;
klninfo->devinfo[dev].lasthashcount = hashcount;
if (maxcount != 0)
klninfo->hashcount += (newhashdev << 32) / maxcount;
}
newhashcount += 0xffffffffull * (uint64_t)klninfo->noncecount;
klninfo->noncecount = 0;
rd_unlock(&(klninfo->stat_lock));
}
return newhashcount;
}
if (klninfo->status == NULL) {
blank_get_statline_before(buf, siz, klncgpu);
return;
}
rd_lock(&(klninfo->stat_lock));
slaves = klninfo->status[0].kline.ws.slavecount;
for (dev = 0; dev <= slaves; dev++) {
if (klninfo->status[dev].kline.ws.temp < temp)
temp = klninfo->status[dev].kline.ws.temp;
fan += klninfo->cfg[dev].kline.cfg.fantarget;
clock += (uint16_t)K_HASHCLOCK(klninfo->cfg[dev].kline.cfg.hashclock);
}
rd_unlock(&(klninfo->stat_lock));
fan /= slaves + 1;
//fan *= 100/255; // <-- You can't do this because int 100 / int 255 == 0
fan = 100 * fan / 255;
if (fan > 100)
fan = 100;
clock /= slaves + 1;
if (clock > 999) // error - so truncate it
clock = 999;
if (klninfo->status == NULL)
return NULL;
rd_lock(&(klninfo->stat_lock));
slaves = klninfo->status[0].kline.ws.slavecount;
for (dev = 0; dev <= slaves; dev++) {
iFan = 0;
if (klninfo->status[dev].kline.ws.fanspeed > 0)
iFan = (unsigned int)TACH_FACTOR / klninfo-
>status[dev].kline.ws.fanspeed;
sprintf(buf, "Fan RPM %d", dev);
root = api_add_int(root, buf, (int *)(&iFan), true);
if (klninfo->devinfo[dev].chipstats != NULL) {
char data[2048];
char one[32];
int n;
rd_unlock(&(klninfo->stat_lock));
return root;
}