From b5ed958e8498facc40ce7ea1e7a3a988cb044b56 Mon Sep 17 00:00:00 2001 From: Kano Date: Wed, 16 May 2012 22:25:21 +1000 Subject: [PATCH 01/29] Icarus: high accuracy timing and other bitstream speed support --- README | 47 +++++ cgminer.c | 17 ++ driver-icarus.c | 499 +++++++++++++++++++++++++++++++++++++++++------- miner.h | 1 + 4 files changed, 496 insertions(+), 68 deletions(-) diff --git a/README b/README index ae554753..5a5609b8 100644 --- a/README +++ b/README @@ -209,6 +209,53 @@ FPGA mining boards(BitForce, Icarus, Ztex) only options: On windows is usually of the format \\.\COMn (where n = the correct device number for the FPGA device) +--icarus-timing Set how the Icarus timing is calculated - one setting/value for all or comma separated + default Use the default Icarus hash time (2.6316ns) + short Calculate the hash time and stop adjusting it at ~315 difficulty 1 shares (~1hr) + long Re-calculate the hash time continuously + value Specify the hash time in nanoseconds (e.g. 2.6316) + + Icarus timing is required for devices that do not exactly match a default Icarus Rev3 in + processing speed + If you have an Icarus Rev3 you should not norally need to use --icarus-timing since the + default values will maximise the MH/s and display it correctly + + Icarus timing is used to determine the number of hashes that have been checked when it aborts + a nonce range (including on a LongPoll) + It is also used to determine the elapsed time when it should abort a nonce range to avoid + letting the Icarus to go idle, but also to safely maximise that time + + 'short' or 'long' mode should only be used on a computer that has enough CPU available to run + cgminer without any CPU delays (an active desktop or swapping computer would not be stable enough) + Any CPU delays while calculating the hash time will affect the result + 'short' mode only requires the computer to be stable until it has completed ~315 difficulty 1 shares + 'long' mode requires it to always be stable to ensure accuracy, however, over time it continually + corrects itself + + When in 'short' or 'long' mode, it will report the hash time value each time it is re-calculated + In 'short' or 'long' mode, the scan abort time starts at 5 seconds and uses the default 2.6316ns + scan hash time, for the first 5 nonce's or one minute (whichever is longer) + + In 'default' or 'value' mode the 'constants' are calculated once at the start, based on the default + value or the value specified + + To determine the hash time value for a non Icarus Rev3 device or an Icarus Rev3 with a different + bitstream to the default one, use 'long' mode and give it at least a few hundred shares, or use + 'short' mode and take note of the final hash time value (Hs) calculated + You can also use the RPC API 'stats' command to see the current hash time (Hs) at any time + + The Icarus code currently only works with a dual FPGA device that supports the same commands as + Icarus Rev3 requires and also is less than ~840MH/s and greater than 2MH/s + If a dual FPGA device does hash faster than ~840MH/s it should work correctly if you supply the + correct hash time nanoseconds value + + The timing code itself will affect the Icarus performance since it increases the delay after + work is completed or aborted until it starts again + The increase is, however, extremely small and the actual increase is reported with the + RPC API 'stats' command (a very slow CPU will make it more noticeable) + Using the 'short' mode will remove this delay after 'short' mode completes + The delay doesn't affect the calculation of the correct hash time + CPU only options (deprecated, not included in binaries!): diff --git a/cgminer.c b/cgminer.c index d8b8e6f0..c5caaca6 100644 --- a/cgminer.c +++ b/cgminer.c @@ -134,6 +134,7 @@ bool opt_api_listen; bool opt_api_network; bool opt_delaynet; bool opt_disable_pool = true; +char *opt_icarus_timing = NULL; char *opt_kernel_path; char *cgminer_path; @@ -675,6 +676,15 @@ static char *set_api_description(const char *arg) return NULL; } +#ifdef USE_ICARUS +static char *set_icarus_timing(const char *arg) +{ + opt_set_charp(arg, &opt_icarus_timing); + + return NULL; +} +#endif + /* These options are available from config file or commandline */ static struct opt_table opt_config_table[] = { #ifdef WANT_CPUMINE @@ -811,6 +821,11 @@ static struct opt_table opt_config_table[] = { OPT_WITH_ARG("--kernel|-k", set_kernel, NULL, NULL, "Override kernel to use (diablo, poclbm, phatk or diakgcn) - one value or comma separated"), +#endif +#ifdef USE_ICARUS + OPT_WITH_ARG("--icarus-timing", + set_icarus_timing, NULL, NULL, + "Set how the Icarus timing is calculated - one setting/value for all or comma separated ('default'/'short'/'long'/value)"), #endif OPT_WITHOUT_ARG("--load-balance", set_loadbalance, &pool_strategy, @@ -2759,6 +2774,8 @@ void write_config(FILE *fcfg) fprintf(fcfg, ",\n\"api-allow\" : \"%s\"", opt_api_allow); if (strcmp(opt_api_description, PACKAGE_STRING) != 0) fprintf(fcfg, ",\n\"api-description\" : \"%s\"", opt_api_description); + if (opt_icarus_timing) + fprintf(fcfg, ",\n\"icarus-timing\" : \"%s\"", opt_icarus_timing); fputs("\n}", fcfg); } diff --git a/driver-icarus.c b/driver-icarus.c index 5ef33009..a4efc967 100644 --- a/driver-icarus.c +++ b/driver-icarus.c @@ -51,17 +51,136 @@ #include "elist.h" #include "miner.h" -// This is valid for a standard Icarus Rev 3 -// Assuming each hash pair takes 5.26ns then a whole nonce range would take 11.3s -// Giving a little leaway 11.1s would be best -//#define ICARUS_READ_COUNT_DEFAULT 111 -#define ICARUS_READ_COUNT_DEFAULT 80 +// The serial I/O speed - Linux uses a define 'B115200' in bits/termios.h +#define ICARUS_IO_SPEED 115200 -// 2 x 11.1 / (5.26 x 10^-9) -//#define ESTIMATE_HASHES 0xFB90365E +// The size of a successful nonce read +#define ICARUS_READ_SIZE 4 -// This is the 8s value -#define ESTIMATE_HASHES 0xB54E9147 +// Ensure the sizes are correct for the Serial read +#if (ICARUS_READ_SIZE != 4) +#error ICARUS_READ_SIZE must be 4 +#endif +#define ASSERT1(condition) __maybe_unused static char sizeof_uint32_t_must_be_4[(condition)?1:-1] +ASSERT1(sizeof(uint32_t) == 4); + +#define ICARUS_READ_TIME ((double)ICARUS_READ_SIZE * (double)8.0 / (double)ICARUS_IO_SPEED) + +// Fraction of a second, USB timeout is measured in +// i.e. 10 means 1/10 of a second +#define TIME_FACTOR 10 +// In Linux it's 10 per second, thus value = 10/TIME_FACTOR = +#define LINUX_TIMEOUT_VALUE 1 +// In Windows it's 1000 per second, thus value = 1000/TIME_FACTOR = +#define WINDOWS_TIMEOUT_VALUE 100 + +// In timing mode: Default starting value until an estimate can be obtained +// 5 seconds allows for up to a ~840MH/s device +#define ICARUS_READ_COUNT_TIMING (5 * TIME_FACTOR) + +// For a standard Icarus REV3 (to 5 places) +// Since this rounds up a the last digit - it is a slight overestimate +// Thus the hash rate will be a VERY slight underestimate +// (by a lot less than the displayed accuracy) +#define ICARUS_HASH_TIME 0.0000000026316 +#define NANOSEC 1000000000.0 + +// Icarus Rev3 doesn't send a completion message when it finishes +// the full nonce range, so to avoid being idle we must abort the +// work (by starting a new work) shortly before it finishes +// +// Thus we need to estimate 2 things: +// 1) How many hashes were done if the work was aborted +// 2) How high can the timeout be before the Icarus is idle, +// to minimise the number of work started +// We set 2) to 'the calculated estimate' - 1 +// to ensure the estimate ends before idle +// +// The simple calculation used is: +// Tn = Total time in seconds to calculate n hashes +// Hs = seconds per hash +// Xn = number of hashes +// W = code overhead per work +// +// Rough but reasonable estimate: +// Tn = Hs * Xn + W (of the form y = mx + b) +// +// Thus: +// Line of best fit (using least squares) +// +// Hs = (n*Sum(XiTi)-Sum(Xi)*Sum(Ti))/(n*Sum(Xi^2)-Sum(Xi)^2) +// W = Sum(Ti)/n - (Hs*Sum(Xi))/n +// +// N.B. W is less when aborting work since we aren't waiting for the reply +// to be transferred back (ICARUS_READ_TIME) +// Calculating the hashes aborted at n seconds is thus just n/Hs +// (though this is still a slight overestimate due to code delays) +// + +// Both below must be exceeded to complete a set of data +// Minimum how long after the first, the last data point must be +#define HISTORY_SEC 60 +// Minimum how many points a single ICARUS_HISTORY should have +#define MIN_DATA_COUNT 5 +// The value above used is doubled each history until it exceeds: +#define MAX_MIN_DATA_COUNT 100 + +static struct timeval history_sec = { HISTORY_SEC, 0 }; + +// Store the last INFO_HISTORY data sets +// [0] = current data, not yet ready to be included as an estimate +// Each new data set throws the last old set off the end thus +// keeping a ongoing average of recent data +#define INFO_HISTORY 10 + +struct ICARUS_HISTORY { + struct timeval finish; + double sumXiTi; + double sumXi; + double sumTi; + double sumXi2; + uint32_t values; + uint32_t hash_count_min; + uint32_t hash_count_max; +}; + +enum timing_mode { MODE_DEFAULT, MODE_SHORT, MODE_LONG, MODE_VALUE }; + +static const char *MODE_DEFAULT_STR = "default"; +static const char *MODE_SHORT_STR = "short"; +static const char *MODE_LONG_STR = "long"; +static const char *MODE_VALUE_STR = "value"; +static const char *MODE_UNKNOWN_STR = "unknown"; + +struct ICARUS_INFO { + // time to calculate the golden_ob + uint64_t golden_hashes; + struct timeval golden_tv; + + struct ICARUS_HISTORY history[INFO_HISTORY+1]; + uint32_t min_data_count; + + // seconds per Hash + double Hs; + int read_count; + + enum timing_mode timing_mode; + bool do_icarus_timing; + + double fullnonce; + int count; + double W; + uint32_t values; + uint64_t hash_count_range; + + // Determine the cost of history processing + // (which will only affect W) + uint64_t history_count; + struct timeval history_time; +}; + +// One for each possible device +static struct ICARUS_INFO *icarus_info[MAX_DEVICES]; struct device_api icarus_api; @@ -98,7 +217,7 @@ static int icarus_open(const char *devpath) ISTRIP | INLCR | IGNCR | ICRNL | IXON); my_termios.c_oflag &= ~OPOST; my_termios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); - my_termios.c_cc[VTIME] = 1; /* block 0.1 second */ + my_termios.c_cc[VTIME] = LINUX_TIMEOUT_VALUE; /* how long to block */ my_termios.c_cc[VMIN] = 0; tcsetattr(serialfd, TCSANOW, &my_termios); @@ -119,7 +238,7 @@ static int icarus_open(const char *devpath) comCfg.dwSize = sizeof(COMMCONFIG); comCfg.wVersion = 1; comCfg.dcb.DCBlength = sizeof(DCB); - comCfg.dcb.BaudRate = 115200; + comCfg.dcb.BaudRate = ICARUS_IO_SPEED; comCfg.dcb.fBinary = 1; comCfg.dcb.fDtrControl = DTR_CONTROL_ENABLE; comCfg.dcb.fRtsControl = RTS_CONTROL_ENABLE; @@ -127,42 +246,52 @@ static int icarus_open(const char *devpath) SetCommConfig(hSerial, &comCfg, sizeof(comCfg)); - // block 0.1 second - COMMTIMEOUTS cto = {100, 0, 100, 0, 100}; + // How long to block + COMMTIMEOUTS cto = {WINDOWS_TIMEOUT_VALUE, 0, WINDOWS_TIMEOUT_VALUE, 0, WINDOWS_TIMEOUT_VALUE}; SetCommTimeouts(hSerial, &cto); return _open_osfhandle((LONG)hSerial, 0); #endif } -static int icarus_gets(unsigned char *buf, size_t bufLen, int fd, int thr_id, int read_count) +static int icarus_gets(unsigned char *buf, int fd, struct timeval *tv_finish, int thr_id, int read_count) { ssize_t ret = 0; int rc = 0; - - while (bufLen) { - ret = read(fd, buf, 1); - if (ret == 1) { - bufLen--; - buf++; + int read_amount = ICARUS_READ_SIZE; + + while (true) { + ret = read(fd, buf, read_amount); + gettimeofday(tv_finish, NULL); + if (ret >= read_amount) + return 0; + + // Allow a partial I/O + if (ret > 0) { + buf += ret; + read_amount -= ret; continue; } - + rc++; if (rc >= read_count) { - applog(LOG_DEBUG, - "Icarus Read: No data in %.2f seconds", (float)(rc/10.0f)); + if (opt_debug) { + applog(LOG_DEBUG, + "Icarus Read: No data in %.2f seconds", + (float)rc/(float)TIME_FACTOR); + } return 1; } if (thr_id >= 0 && work_restart[thr_id].restart) { - applog(LOG_DEBUG, - "Icarus Read: Work restart at %.2f seconds", (float)(rc/10.0f)); + if (opt_debug) { + applog(LOG_DEBUG, + "Icarus Read: Work restart at %.2f seconds", + (float)(rc)/(float)TIME_FACTOR); + } return 1; } } - - return 0; } static int icarus_write(int fd, const void *buf, size_t bufLen) @@ -178,8 +307,94 @@ static int icarus_write(int fd, const void *buf, size_t bufLen) #define icarus_close(fd) close(fd) +static const char *timing_mode_str(enum timing_mode timing_mode) +{ + switch(timing_mode) { + case MODE_DEFAULT: + return MODE_DEFAULT_STR; + case MODE_SHORT: + return MODE_SHORT_STR; + case MODE_LONG: + return MODE_LONG_STR; + case MODE_VALUE: + return MODE_VALUE_STR; + default: + return MODE_UNKNOWN_STR; + } +} + +static void set_timing_mode(struct cgpu_info *icarus) +{ + struct ICARUS_INFO *info = icarus_info[icarus->device_id]; + double Hs; + char buf[BUFSIZ+1]; + char *ptr, *comma; + size_t max; + int i; + + if (opt_icarus_timing == NULL) + buf[0] = '\0'; + else { + ptr = opt_icarus_timing; + for (i = 0; i < icarus->device_id; i++) { + comma = strchr(ptr, ','); + if (comma == NULL) + break; + ptr = comma + 1; + } + + comma = strchr(ptr, ','); + if (comma == NULL) + max = strlen(ptr); + else + max = comma - ptr; + + if (max > BUFSIZ) + max = BUFSIZ; + strncpy(buf, ptr, max); + buf[max] = '\0'; + } + + if (strcasecmp(buf, MODE_SHORT_STR) == 0) { + info->Hs = ICARUS_HASH_TIME; + info->read_count = ICARUS_READ_COUNT_TIMING; + + info->timing_mode = MODE_SHORT; + info->do_icarus_timing = true; + } else if (strcasecmp(buf, MODE_LONG_STR) == 0) { + info->Hs = ICARUS_HASH_TIME; + info->read_count = ICARUS_READ_COUNT_TIMING; + + info->timing_mode = MODE_LONG; + info->do_icarus_timing = true; + } else if ((Hs = atof(buf)) != 0) { + info->Hs = Hs / NANOSEC; + info->fullnonce = info->Hs * (((double)0xffffffff) + 1); + info->read_count = (int)(info->fullnonce * TIME_FACTOR) - 1; + + info->timing_mode = MODE_VALUE; + info->do_icarus_timing = false; + } else { + // Anything else in buf just uses DEFAULT mode + + info->Hs = ICARUS_HASH_TIME; + info->fullnonce = info->Hs * (((double)0xffffffff) + 1); + info->read_count = (int)(info->fullnonce * TIME_FACTOR) - 1; + + info->timing_mode = MODE_DEFAULT; + info->do_icarus_timing = false; + } + + info->min_data_count = MIN_DATA_COUNT; + + applog(LOG_DEBUG, "Icarus: Init: %d mode=%s read_count=%d Hs=%e", + icarus->device_id, timing_mode_str(info->timing_mode), info->read_count, info->Hs); + +} + static bool icarus_detect_one(const char *devpath) { + struct ICARUS_INFO *info; struct timeval tv_start, tv_finish; int fd; @@ -194,8 +409,9 @@ static bool icarus_detect_one(const char *devpath) "0000000087320b1a1426674f2fa722ce"; const char golden_nonce[] = "000187a2"; + const uint32_t golden_nonce_val = 0x000187a2; - unsigned char ob_bin[64], nonce_bin[4]; + unsigned char ob_bin[64], nonce_bin[ICARUS_READ_SIZE]; char *nonce_hex; if (total_devices == MAX_DEVICES) @@ -212,8 +428,7 @@ static bool icarus_detect_one(const char *devpath) gettimeofday(&tv_start, NULL); memset(nonce_bin, 0, sizeof(nonce_bin)); - icarus_gets(nonce_bin, sizeof(nonce_bin), fd, -1, 1); - gettimeofday(&tv_finish, NULL); + icarus_gets(nonce_bin, fd, &tv_finish, -1, 1); icarus_close(fd); @@ -221,16 +436,16 @@ static bool icarus_detect_one(const char *devpath) if (nonce_hex) { if (strncmp(nonce_hex, golden_nonce, 8)) { applog(LOG_ERR, - "Icarus Detect: " - "Test failed at %s: get %s, should: %s", - devpath, nonce_hex, golden_nonce); + "Icarus Detect: " + "Test failed at %s: get %s, should: %s", + devpath, nonce_hex, golden_nonce); free(nonce_hex); return false; } applog(LOG_DEBUG, - "Icarus Detect: " - "Test succeeded at %s: got %s", - devpath, nonce_hex); + "Icarus Detect: " + "Test succeeded at %s: got %s", + devpath, nonce_hex); free(nonce_hex); } else return false; @@ -244,7 +459,23 @@ static bool icarus_detect_one(const char *devpath) add_cgpu(icarus); applog(LOG_INFO, "Found Icarus at %s, mark as %d", - devpath, icarus->device_id); + devpath, icarus->device_id); + + if (icarus_info[icarus->device_id] == NULL) { + icarus_info[icarus->device_id] = (struct ICARUS_INFO *)malloc(sizeof(struct ICARUS_INFO)); + if (unlikely(!(icarus_info[icarus->device_id]))) + quit(1, "Failed to malloc ICARUS_INFO"); + } + + info = icarus_info[icarus->device_id]; + + // Initialise everything to zero for a new device + memset(info, 0, sizeof(struct ICARUS_INFO)); + + info->golden_hashes = (golden_nonce_val & 0x7fffffff) << 1; + timersub(&tv_finish, &tv_start, &(info->golden_tv)); + + set_timing_mode(icarus); return true; } @@ -295,11 +526,24 @@ static uint64_t icarus_scanhash(struct thr_info *thr, struct work *work, int fd; int ret; - unsigned char ob_bin[64], nonce_bin[4]; - char *ob_hex, *nonce_hex; + struct ICARUS_INFO *info; + + unsigned char ob_bin[64], nonce_bin[ICARUS_READ_SIZE]; + char *ob_hex; uint32_t nonce; - uint32_t hash_count; + uint64_t hash_count; struct timeval tv_start, tv_finish, elapsed; + struct timeval tv_history_start, tv_history_finish; + double Ti, Xi; + int i; + + struct ICARUS_HISTORY *history0, *history; + int count; + double Hs, W, fullnonce; + int read_count; + uint64_t estimate_hashes; + uint32_t values; + uint64_t hash_count_range; elapsed.tv_sec = elapsed.tv_usec = 0; @@ -315,39 +559,51 @@ static uint64_t icarus_scanhash(struct thr_info *thr, struct work *work, tcflush(fd, TCOFLUSH); #endif ret = icarus_write(fd, ob_bin, sizeof(ob_bin)); - if (opt_debug) - gettimeofday(&tv_start, NULL); if (ret) return 0; /* This should never happen */ + info = icarus_info[icarus->device_id]; + + if (opt_debug || info->do_icarus_timing) + gettimeofday(&tv_start, NULL); + if (opt_debug) { ob_hex = bin2hex(ob_bin, sizeof(ob_bin)); if (ob_hex) { applog(LOG_DEBUG, "Icarus %d sent: %s", - icarus->device_id, ob_hex); + icarus->device_id, ob_hex); free(ob_hex); } } - /* Icarus will return 4 bytes nonces or nothing */ + /* Icarus will return 4 bytes (ICARUS_READ_SIZE) nonces or nothing */ memset(nonce_bin, 0, sizeof(nonce_bin)); - ret = icarus_gets(nonce_bin, sizeof(nonce_bin), fd, thr_id, - ICARUS_READ_COUNT_DEFAULT); - - if (opt_debug) - gettimeofday(&tv_finish, NULL); + ret = icarus_gets(nonce_bin, fd, &tv_finish, thr_id, info->read_count); work->blk.nonce = 0xffffffff; memcpy((char *)&nonce, nonce_bin, sizeof(nonce_bin)); // aborted before becoming idle, get new work - if (nonce == 0 && ret) { + if (nonce == 0 && ret) { + timersub(&tv_finish, &tv_start, &elapsed); + + // ONLY up to just when it aborted + // We didn't read a reply so we don't subtract ICARUS_READ_TIME + estimate_hashes = ((double)(elapsed.tv_sec) + + ((double)(elapsed.tv_usec))/((double)1000000)) / info->Hs; + + // If some Serial-USB delay allowed the full nonce range to + // complete it can't have done more than a full nonce + if (unlikely(estimate_hashes > 0xffffffff)) + estimate_hashes = 0xffffffff; + if (opt_debug) { - timersub(&tv_finish, &tv_start, &elapsed); - applog(LOG_DEBUG, "Icarus %d no nonce = 0x%08x hashes (%ld.%06lds)", - icarus->device_id, ESTIMATE_HASHES, elapsed.tv_sec, elapsed.tv_usec); + applog(LOG_DEBUG, "Icarus %d no nonce = 0x%08llx hashes (%ld.%06lds)", + icarus->device_id, estimate_hashes, + elapsed.tv_sec, elapsed.tv_usec); } - return ESTIMATE_HASHES; + + return estimate_hashes; } #if !defined (__BIG_ENDIAN__) && !defined(MIPSEB) @@ -356,28 +612,134 @@ static uint64_t icarus_scanhash(struct thr_info *thr, struct work *work, submit_nonce(thr, work, nonce); - if (opt_debug) { - timersub(&tv_finish, &tv_start, &elapsed); - - nonce_hex = bin2hex(nonce_bin, sizeof(nonce_bin)); - if (nonce_hex) { - applog(LOG_DEBUG, "Icarus %d returned (elapsed %ld.%06ld seconds): %s", - icarus->device_id, elapsed.tv_sec, elapsed.tv_usec, nonce_hex); - free(nonce_hex); - } - } - hash_count = (nonce & 0x7fffffff); if (hash_count++ == 0x7fffffff) hash_count = 0xffffffff; else hash_count <<= 1; - if (opt_debug) - applog(LOG_DEBUG, "Icarus %d nonce = 0x%08x = 0x%08x hashes (%ld.%06lds)", + if (opt_debug || info->do_icarus_timing) + timersub(&tv_finish, &tv_start, &elapsed); + + if (opt_debug) { + applog(LOG_DEBUG, "Icarus %d nonce = 0x%08x = 0x%08llx hashes (%ld.%06lds)", icarus->device_id, nonce, hash_count, elapsed.tv_sec, elapsed.tv_usec); + } + + // ignore possible end condition values + if (info->do_icarus_timing && (nonce & 0x7fffffff) > 0x000fffff && (nonce & 0x7fffffff) < 0x7ff00000) { + gettimeofday(&tv_history_start, NULL); + + history0 = &(info->history[0]); + + if (history0->values == 0) + timeradd(&tv_start, &history_sec, &(history0->finish)); + + Ti = (double)(elapsed.tv_sec) + + ((double)(elapsed.tv_usec))/((double)1000000) + - ICARUS_READ_TIME; + Xi = (double)hash_count; + history0->sumXiTi += Xi * Ti; + history0->sumXi += Xi; + history0->sumTi += Ti; + history0->sumXi2 += Xi * Xi; + + history0->values++; + + if (history0->hash_count_max < hash_count) + history0->hash_count_max = hash_count; + if (history0->hash_count_min > hash_count || history0->hash_count_min == 0) + history0->hash_count_min = hash_count; + + if (history0->values >= info->min_data_count + && timercmp(&tv_start, &(history0->finish), >)) { + for (i = INFO_HISTORY; i > 0; i--) + memcpy(&(info->history[i]), &(info->history[i-1]), sizeof(struct ICARUS_HISTORY)); + + // Initialise history0 to zero for summary calculation + memset(history0, 0, sizeof(struct ICARUS_HISTORY)); + + // We just completed a history data set + // So now recalc read_count based on the whole history thus we will + // initially get more accurate until it completes INFO_HISTORY + // total data sets + count = 0; + for (i = 1 ; i <= INFO_HISTORY; i++) { + history = &(info->history[i]); + if (history->values >= MIN_DATA_COUNT) { + count++; + + history0->sumXiTi += history->sumXiTi; + history0->sumXi += history->sumXi; + history0->sumTi += history->sumTi; + history0->sumXi2 += history->sumXi2; + history0->values += history->values; + + if (history0->hash_count_max < history->hash_count_max) + history0->hash_count_max = history->hash_count_max; + if (history0->hash_count_min > history->hash_count_min || history0->hash_count_min == 0) + history0->hash_count_min = history->hash_count_min; + } + } + + // All history data + Hs = (history0->values*history0->sumXiTi - history0->sumXi*history0->sumTi) + / (history0->values*history0->sumXi2 - history0->sumXi*history0->sumXi); + W = history0->sumTi/history0->values - Hs*history0->sumXi/history0->values; + hash_count_range = history0->hash_count_max - history0->hash_count_min; + values = history0->values; + + // Initialise history0 to zero for next data set + memset(history0, 0, sizeof(struct ICARUS_HISTORY)); + + fullnonce = W + Hs * (((double)0xffffffff) + 1); + read_count = (int)(fullnonce * TIME_FACTOR) - 1; + + info->Hs = Hs; + info->read_count = read_count; + + info->fullnonce = fullnonce; + info->count = count; + info->W = W; + info->values = values; + info->hash_count_range = hash_count_range; + + if (info->min_data_count < MAX_MIN_DATA_COUNT) + info->min_data_count *= 2; + else if (info->timing_mode == MODE_SHORT) + info->do_icarus_timing = false; + +// applog(LOG_WARNING, "Icarus %d Re-estimate: read_count=%d fullnonce=%fs history count=%d Hs=%e W=%e values=%d hash range=0x%08lx min data count=%u", icarus->device_id, read_count, fullnonce, count, Hs, W, values, hash_count_range, info->min_data_count); + applog(LOG_WARNING, "Icarus %d Re-estimate: Hs=%e W=%e read_count=%d fullnonce=%.3fs", + icarus->device_id, Hs, W, read_count, fullnonce); + } + info->history_count++; + gettimeofday(&tv_history_finish, NULL); + + timersub(&tv_history_finish, &tv_history_start, &tv_history_finish); + timeradd(&tv_history_finish, &(info->history_time), &(info->history_time)); + } + + return hash_count; +} - return hash_count; +static void icarus_api_stats(char *buf, struct cgpu_info *cgpu, bool isjson) +{ + struct ICARUS_INFO *info = icarus_info[cgpu->device_id]; + + // Warning, access to these is not locked - but we don't really + // care since hashing performance is way more important than + // locking access to displaying API debug 'stats' + sprintf(buf, isjson + ? "\"read_count\":%d,\"fullnonce\":%f,\"count\":%d,\"Hs\":%.15f,\"W\":%f,\"total_values\":%u,\"range\":%ld,\"history_count\":%lu,\"history_time\":%f,\"min_data_count\":%u,\"timing_values\":%u" + : "read_count=%d,fullnonce=%f,count=%d,Hs=%.15f,W=%f,total_values=%u,range=%ld,history_count=%lu,history_time=%f,min_data_count=%u,timing_values=%u", + info->read_count, info->fullnonce, + info->count, info->Hs, info->W, + info->values, info->hash_count_range, + info->history_count, + (double)(info->history_time.tv_sec) + + ((double)(info->history_time.tv_usec))/((double)1000000), + info->min_data_count, info->history[0].values); } static void icarus_shutdown(struct thr_info *thr) @@ -390,6 +752,7 @@ struct device_api icarus_api = { .dname = "icarus", .name = "ICA", .api_detect = icarus_detect, + .get_api_stats = icarus_api_stats, .thread_prepare = icarus_prepare, .scanhash = icarus_scanhash, .thread_shutdown = icarus_shutdown, diff --git a/miner.h b/miner.h index 186eb9f8..2a793a18 100644 --- a/miner.h +++ b/miner.h @@ -514,6 +514,7 @@ extern bool opt_api_listen; extern bool opt_api_network; extern bool opt_delaynet; extern bool opt_restart; +extern char *opt_icarus_timing; extern pthread_rwlock_t netacc_lock; From 17b6f111a66b8ccfcaf158c65cb0adaa1140b42a Mon Sep 17 00:00:00 2001 From: Kano Date: Wed, 16 May 2012 23:53:13 +1000 Subject: [PATCH 02/29] Icarus: make --icarus-timing hidden and document it in FPGA-README --- FPGA-README | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++++ README | 47 +--------------------------------------------- cgminer.c | 2 +- 3 files changed, 56 insertions(+), 47 deletions(-) create mode 100644 FPGA-README diff --git a/FPGA-README b/FPGA-README new file mode 100644 index 00000000..9044dc84 --- /dev/null +++ b/FPGA-README @@ -0,0 +1,54 @@ + +This README contains extended details about FPGA mining with cgminer + + +Icarus + +There is a hidden option in cgminer when Icarus support is compiled in: + +--icarus-timing Set how the Icarus timing is calculated - one setting/value for all or comma separated + default Use the default Icarus hash time (2.6316ns) + short Calculate the hash time and stop adjusting it at ~315 difficulty 1 shares (~1hr) + long Re-calculate the hash time continuously + value Specify the hash time in nanoseconds (e.g. 2.6316) + + Icarus timing is required for devices that do not exactly match a default Icarus Rev3 in + processing speed + If you have an Icarus Rev3 you should not norally need to use --icarus-timing since the + default values will maximise the MH/s and display it correctly + + Icarus timing is used to determine the number of hashes that have been checked when it aborts + a nonce range (including on a LongPoll) + It is also used to determine the elapsed time when it should abort a nonce range to avoid + letting the Icarus go idle, but also to safely maximise that time + + 'short' or 'long' mode should only be used on a computer that has enough CPU available to run + cgminer without any CPU delays (an active desktop or swapping computer would not be stable enough) + Any CPU delays while calculating the hash time will affect the result + 'short' mode only requires the computer to be stable until it has completed ~315 difficulty 1 shares + 'long' mode requires it to always be stable to ensure accuracy, however, over time it continually + corrects itself + + When in 'short' or 'long' mode, it will report the hash time value each time it is re-calculated + In 'short' or 'long' mode, the scan abort time starts at 5 seconds and uses the default 2.6316ns + scan hash time, for the first 5 nonce's or one minute (whichever is longer) + + In 'default' or 'value' mode the 'constants' are calculated once at the start, based on the default + value or the value specified + + To determine the hash time value for a non Icarus Rev3 device or an Icarus Rev3 with a different + bitstream to the default one, use 'long' mode and give it at least a few hundred shares, or use + 'short' mode and take note of the final hash time value (Hs) calculated + You can also use the RPC API 'stats' command to see the current hash time (Hs) at any time + + The Icarus code currently only works with a dual FPGA device that supports the same commands as + Icarus Rev3 requires and also is less than ~840MH/s and greater than 2MH/s + If a dual FPGA device does hash faster than ~840MH/s it should work correctly if you supply the + correct hash time nanoseconds value + + The timing code itself will affect the Icarus performance since it increases the delay after + work is completed or aborted until it starts again + The increase is, however, extremely small and the actual increase is reported with the + RPC API 'stats' command (a very slow CPU will make it more noticeable) + Using the 'short' mode will remove this delay after 'short' mode completes + The delay doesn't affect the calculation of the correct hash time diff --git a/README b/README index 5a5609b8..9c445e84 100644 --- a/README +++ b/README @@ -209,52 +209,7 @@ FPGA mining boards(BitForce, Icarus, Ztex) only options: On windows is usually of the format \\.\COMn (where n = the correct device number for the FPGA device) ---icarus-timing Set how the Icarus timing is calculated - one setting/value for all or comma separated - default Use the default Icarus hash time (2.6316ns) - short Calculate the hash time and stop adjusting it at ~315 difficulty 1 shares (~1hr) - long Re-calculate the hash time continuously - value Specify the hash time in nanoseconds (e.g. 2.6316) - - Icarus timing is required for devices that do not exactly match a default Icarus Rev3 in - processing speed - If you have an Icarus Rev3 you should not norally need to use --icarus-timing since the - default values will maximise the MH/s and display it correctly - - Icarus timing is used to determine the number of hashes that have been checked when it aborts - a nonce range (including on a LongPoll) - It is also used to determine the elapsed time when it should abort a nonce range to avoid - letting the Icarus to go idle, but also to safely maximise that time - - 'short' or 'long' mode should only be used on a computer that has enough CPU available to run - cgminer without any CPU delays (an active desktop or swapping computer would not be stable enough) - Any CPU delays while calculating the hash time will affect the result - 'short' mode only requires the computer to be stable until it has completed ~315 difficulty 1 shares - 'long' mode requires it to always be stable to ensure accuracy, however, over time it continually - corrects itself - - When in 'short' or 'long' mode, it will report the hash time value each time it is re-calculated - In 'short' or 'long' mode, the scan abort time starts at 5 seconds and uses the default 2.6316ns - scan hash time, for the first 5 nonce's or one minute (whichever is longer) - - In 'default' or 'value' mode the 'constants' are calculated once at the start, based on the default - value or the value specified - - To determine the hash time value for a non Icarus Rev3 device or an Icarus Rev3 with a different - bitstream to the default one, use 'long' mode and give it at least a few hundred shares, or use - 'short' mode and take note of the final hash time value (Hs) calculated - You can also use the RPC API 'stats' command to see the current hash time (Hs) at any time - - The Icarus code currently only works with a dual FPGA device that supports the same commands as - Icarus Rev3 requires and also is less than ~840MH/s and greater than 2MH/s - If a dual FPGA device does hash faster than ~840MH/s it should work correctly if you supply the - correct hash time nanoseconds value - - The timing code itself will affect the Icarus performance since it increases the delay after - work is completed or aborted until it starts again - The increase is, however, extremely small and the actual increase is reported with the - RPC API 'stats' command (a very slow CPU will make it more noticeable) - Using the 'short' mode will remove this delay after 'short' mode completes - The delay doesn't affect the calculation of the correct hash time +For other FPGA details see the FPGA-README CPU only options (deprecated, not included in binaries!): diff --git a/cgminer.c b/cgminer.c index c5caaca6..b6b0bae2 100644 --- a/cgminer.c +++ b/cgminer.c @@ -825,7 +825,7 @@ static struct opt_table opt_config_table[] = { #ifdef USE_ICARUS OPT_WITH_ARG("--icarus-timing", set_icarus_timing, NULL, NULL, - "Set how the Icarus timing is calculated - one setting/value for all or comma separated ('default'/'short'/'long'/value)"), + opt_hidden), #endif OPT_WITHOUT_ARG("--load-balance", set_loadbalance, &pool_strategy, From af85ed48c26ef67bfdfba68993ed2f78a9f641f6 Mon Sep 17 00:00:00 2001 From: Kano Date: Wed, 16 May 2012 23:57:38 +1000 Subject: [PATCH 03/29] FPGA-README spelling --- FPGA-README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FPGA-README b/FPGA-README index 9044dc84..203eaf2d 100644 --- a/FPGA-README +++ b/FPGA-README @@ -14,7 +14,7 @@ There is a hidden option in cgminer when Icarus support is compiled in: Icarus timing is required for devices that do not exactly match a default Icarus Rev3 in processing speed - If you have an Icarus Rev3 you should not norally need to use --icarus-timing since the + If you have an Icarus Rev3 you should not normally need to use --icarus-timing since the default values will maximise the MH/s and display it correctly Icarus timing is used to determine the number of hashes that have been checked when it aborts From f15d2233fa57ef9f06f7ed60c67f31afca44e2c9 Mon Sep 17 00:00:00 2001 From: Kano Date: Tue, 22 May 2012 09:42:43 +1000 Subject: [PATCH 04/29] Icarus: fix abort calculation/allow user specified abort --- FPGA-README | 6 ++++-- driver-icarus.c | 51 ++++++++++++++++++++++++++++++++++--------------- 2 files changed, 40 insertions(+), 17 deletions(-) diff --git a/FPGA-README b/FPGA-README index 203eaf2d..79725141 100644 --- a/FPGA-README +++ b/FPGA-README @@ -7,10 +7,10 @@ Icarus There is a hidden option in cgminer when Icarus support is compiled in: --icarus-timing Set how the Icarus timing is calculated - one setting/value for all or comma separated - default Use the default Icarus hash time (2.6316ns) + default[=N] Use the default Icarus hash time (2.6316ns) short Calculate the hash time and stop adjusting it at ~315 difficulty 1 shares (~1hr) long Re-calculate the hash time continuously - value Specify the hash time in nanoseconds (e.g. 2.6316) + value[=N] Specify the hash time in nanoseconds (e.g. 2.6316) and abort time (e.g. 2.6316=80) Icarus timing is required for devices that do not exactly match a default Icarus Rev3 in processing speed @@ -35,6 +35,8 @@ There is a hidden option in cgminer when Icarus support is compiled in: In 'default' or 'value' mode the 'constants' are calculated once at the start, based on the default value or the value specified + The optional additional =N specifies to set the default abort at N 1/10ths of a second, not the + calculated value, which is 112 for 2.6316ns To determine the hash time value for a non Icarus Rev3 device or an Icarus Rev3 with a different bitstream to the default one, use 'long' mode and give it at least a few hundred shares, or use diff --git a/driver-icarus.c b/driver-icarus.c index a4efc967..eb4d03a0 100644 --- a/driver-icarus.c +++ b/driver-icarus.c @@ -82,7 +82,7 @@ ASSERT1(sizeof(uint32_t) == 4); // Since this rounds up a the last digit - it is a slight overestimate // Thus the hash rate will be a VERY slight underestimate // (by a lot less than the displayed accuracy) -#define ICARUS_HASH_TIME 0.0000000026316 +#define ICARUS_REV3_HASH_TIME 0.0000000026316 #define NANOSEC 1000000000.0 // Icarus Rev3 doesn't send a completion message when it finishes @@ -259,17 +259,22 @@ static int icarus_gets(unsigned char *buf, int fd, struct timeval *tv_finish, in ssize_t ret = 0; int rc = 0; int read_amount = ICARUS_READ_SIZE; + bool first = true; + // Read reply 1 byte at a time to get earliest tv_finish while (true) { - ret = read(fd, buf, read_amount); - gettimeofday(tv_finish, NULL); + ret = read(fd, buf, 1); + + if (first) + gettimeofday(tv_finish, NULL); + if (ret >= read_amount) return 0; - // Allow a partial I/O if (ret > 0) { buf += ret; read_amount -= ret; + first = false; continue; } @@ -328,7 +333,7 @@ static void set_timing_mode(struct cgpu_info *icarus) struct ICARUS_INFO *info = icarus_info[icarus->device_id]; double Hs; char buf[BUFSIZ+1]; - char *ptr, *comma; + char *ptr, *comma, *eq; size_t max; int i; @@ -355,14 +360,17 @@ static void set_timing_mode(struct cgpu_info *icarus) buf[max] = '\0'; } + info->Hs = 0; + info->read_count = 0; + if (strcasecmp(buf, MODE_SHORT_STR) == 0) { - info->Hs = ICARUS_HASH_TIME; + info->Hs = ICARUS_REV3_HASH_TIME; info->read_count = ICARUS_READ_COUNT_TIMING; info->timing_mode = MODE_SHORT; info->do_icarus_timing = true; } else if (strcasecmp(buf, MODE_LONG_STR) == 0) { - info->Hs = ICARUS_HASH_TIME; + info->Hs = ICARUS_REV3_HASH_TIME; info->read_count = ICARUS_READ_COUNT_TIMING; info->timing_mode = MODE_LONG; @@ -370,16 +378,29 @@ static void set_timing_mode(struct cgpu_info *icarus) } else if ((Hs = atof(buf)) != 0) { info->Hs = Hs / NANOSEC; info->fullnonce = info->Hs * (((double)0xffffffff) + 1); - info->read_count = (int)(info->fullnonce * TIME_FACTOR) - 1; + + if ((eq = strchr(buf, '=')) != NULL) + info->read_count = atoi(eq+1); + + if (info->read_count < 1) + info->read_count = (int)(info->fullnonce * TIME_FACTOR) - 1; + + if (unlikely(info->read_count < 1)) + info->read_count = 1; info->timing_mode = MODE_VALUE; info->do_icarus_timing = false; } else { // Anything else in buf just uses DEFAULT mode - info->Hs = ICARUS_HASH_TIME; + info->Hs = ICARUS_REV3_HASH_TIME; info->fullnonce = info->Hs * (((double)0xffffffff) + 1); - info->read_count = (int)(info->fullnonce * TIME_FACTOR) - 1; + + if ((eq = strchr(buf, '=')) != NULL) + info->read_count = atoi(eq+1); + + if (info->read_count < 1) + info->read_count = (int)(info->fullnonce * TIME_FACTOR) - 1; info->timing_mode = MODE_DEFAULT; info->do_icarus_timing = false; @@ -562,10 +583,7 @@ static uint64_t icarus_scanhash(struct thr_info *thr, struct work *work, if (ret) return 0; /* This should never happen */ - info = icarus_info[icarus->device_id]; - - if (opt_debug || info->do_icarus_timing) - gettimeofday(&tv_start, NULL); + gettimeofday(&tv_start, NULL); if (opt_debug) { ob_hex = bin2hex(ob_bin, sizeof(ob_bin)); @@ -578,6 +596,7 @@ static uint64_t icarus_scanhash(struct thr_info *thr, struct work *work, /* Icarus will return 4 bytes (ICARUS_READ_SIZE) nonces or nothing */ memset(nonce_bin, 0, sizeof(nonce_bin)); + info = icarus_info[icarus->device_id]; ret = icarus_gets(nonce_bin, fd, &tv_finish, thr_id, info->read_count); work->blk.nonce = 0xffffffff; @@ -654,7 +673,9 @@ static uint64_t icarus_scanhash(struct thr_info *thr, struct work *work, if (history0->values >= info->min_data_count && timercmp(&tv_start, &(history0->finish), >)) { for (i = INFO_HISTORY; i > 0; i--) - memcpy(&(info->history[i]), &(info->history[i-1]), sizeof(struct ICARUS_HISTORY)); + memcpy(&(info->history[i]), + &(info->history[i-1]), + sizeof(struct ICARUS_HISTORY)); // Initialise history0 to zero for summary calculation memset(history0, 0, sizeof(struct ICARUS_HISTORY)); From 07212db106a1cc654affe3b95bf8acff7b8ed752 Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Thu, 31 May 2012 08:34:06 +1000 Subject: [PATCH 05/29] Restarting cgminer from within after ADL has been corrupted only leads to a crash. Display a warning only and disable fanspeed monitoring. --- adl.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/adl.c b/adl.c index 837f24c2..6ed6a182 100644 --- a/adl.c +++ b/adl.c @@ -692,11 +692,7 @@ int gpu_fanpercent(int gpu) unlock_adl(); if (unlikely(ga->has_fanspeed && ret == -1)) { applog(LOG_WARNING, "GPU %d stopped reporting fanspeed due to driver corruption", gpu); - if (opt_restart) { - applog(LOG_WARNING, "Restart enabled, will restart cgminer"); - applog(LOG_WARNING, "You can disable this with the --no-restart option"); - app_restart(); - } + applog(LOG_WARNING, "You will need to start cgminer from scratch to correct this"); applog(LOG_WARNING, "Disabling fanspeed monitoring on this device"); ga->has_fanspeed = false; } From 85210ee7a897d8132011881cc721cf33668287d0 Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Thu, 31 May 2012 08:41:56 +1000 Subject: [PATCH 06/29] If shares are known stale, don't use them to decide to disable a pool for sequential rejects. --- cgminer.c | 7 +++++-- miner.h | 2 ++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/cgminer.c b/cgminer.c index 118747b6..2373cae6 100644 --- a/cgminer.c +++ b/cgminer.c @@ -1725,8 +1725,10 @@ static bool submit_upstream_work(const struct work *work, CURL *curl) /* Once we have more than a nominal amount of sequential rejects, * at least 10 and more than 3 mins at the current utility, * disable the pool because some pool error is likely to have - * ensued. */ - if (pool->seq_rejects > 10 && opt_disable_pool && total_pools > 1) { + * ensued. Do not do this if we know the share just happened to + * be stale due to networking delays. + */ + if (pool->seq_rejects > 10 && !work->stale && opt_disable_pool && total_pools > 1) { double utility = total_accepted / ( total_secs ? total_secs : 1 ) * 60; if (pool->seq_rejects > utility * 3) { @@ -2161,6 +2163,7 @@ static void *submit_work_thread(void *userdata) pool->stale_shares++; goto out; } + work->stale = true; } ce = pop_curl_entry(pool); diff --git a/miner.h b/miner.h index 186eb9f8..a9adafee 100644 --- a/miner.h +++ b/miner.h @@ -716,11 +716,13 @@ struct work { int thr_id; struct pool *pool; struct timeval tv_staged; + bool mined; bool clone; bool cloned; bool rolltime; bool longpoll; + bool stale; unsigned int work_block; int id; From 2586bda59a3c668c56987472c2f6785296e762aa Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Thu, 31 May 2012 09:40:32 +1000 Subject: [PATCH 07/29] Use only one longpoll as much as possible by using a pthread conditional broadcast that each longpoll thread waits on and checks if it's the current pool before opening its longpoll connection. --- cgminer.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/cgminer.c b/cgminer.c index f460796f..42c771e3 100644 --- a/cgminer.c +++ b/cgminer.c @@ -165,6 +165,9 @@ static pthread_rwlock_t blk_lock; pthread_rwlock_t netacc_lock; +static pthread_mutex_t lp_lock; +static pthread_cond_t lp_cond; + double total_mhashes_done; static struct timeval total_tv_start, total_tv_end; @@ -2283,6 +2286,10 @@ void switch_pools(struct pool *selected) mutex_lock(&qd_lock); total_queued = 0; mutex_unlock(&qd_lock); + + mutex_lock(&lp_lock); + pthread_cond_broadcast(&lp_cond); + mutex_unlock(&lp_lock); } static void discard_work(struct work *work) @@ -3993,6 +4000,14 @@ static struct pool *select_longpoll_pool(struct pool *cp) return NULL; } +static void wait_lpcurrent(struct pool *pool) +{ + while (pool != current_pool()) { + mutex_lock(&lp_lock); + pthread_cond_wait(&lp_cond, &lp_lock); + } +} + static void *longpoll_thread(void *userdata) { struct pool *cp = (struct pool *)userdata; @@ -4023,6 +4038,8 @@ retry_pool: /* Any longpoll from any pool is enough for this to be true */ have_longpoll = true; + wait_lpcurrent(cp); + if (cp == pool) applog(LOG_WARNING, "Long-polling activated for %s", pool->lp_url); else @@ -4031,6 +4048,8 @@ retry_pool: while (42) { json_t *val, *soval; + wait_lpcurrent(cp); + gettimeofday(&start, NULL); /* Longpoll connections can be persistent for a very long time @@ -4726,6 +4745,10 @@ int main(int argc, char *argv[]) rwlock_init(&blk_lock); rwlock_init(&netacc_lock); + mutex_init(&lp_lock); + if (unlikely(pthread_cond_init(&lp_cond, NULL))) + quit(1, "Failed to pthread_cond_init lp_cond"); + sprintf(packagename, "%s %s", PACKAGE, VERSION); #ifdef WANT_CPUMINE From 624948e246e9565e9a5ef932f4a1cb25b018efb1 Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Thu, 31 May 2012 09:44:51 +1000 Subject: [PATCH 08/29] Open a longpoll connection if a pool is in the REJECTING state as it's the only way to re-enable it automatically. --- cgminer.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cgminer.c b/cgminer.c index 42c771e3..fac4a04d 100644 --- a/cgminer.c +++ b/cgminer.c @@ -4000,8 +4000,14 @@ static struct pool *select_longpoll_pool(struct pool *cp) return NULL; } +/* This will make the longpoll thread wait till it's the current pool, or it + * has been flagged as rejecting, before attempting to open any connections. + */ static void wait_lpcurrent(struct pool *pool) { + if (pool->enabled == POOL_REJECTING) + return; + while (pool != current_pool()) { mutex_lock(&lp_lock); pthread_cond_wait(&lp_cond, &lp_lock); From 85bddde1c02a57d7c1a06b9fe831b2e1c320f2ca Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Thu, 31 May 2012 19:20:40 +1000 Subject: [PATCH 09/29] Must unlock mutex after waking up on pthread conditional. --- cgminer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/cgminer.c b/cgminer.c index b71edc06..ea04424e 100644 --- a/cgminer.c +++ b/cgminer.c @@ -4029,6 +4029,7 @@ static void wait_lpcurrent(struct pool *pool) mutex_lock(&lp_lock); pthread_cond_wait(&lp_cond, &lp_lock); } + mutex_unlock(&lp_lock); } static void *longpoll_thread(void *userdata) From 84064a2b1e5ac77f4e753d97ae74c9d031d69598 Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Thu, 31 May 2012 19:25:33 +1000 Subject: [PATCH 10/29] Must unlock mutex within loop. --- cgminer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cgminer.c b/cgminer.c index ea04424e..9565fc1b 100644 --- a/cgminer.c +++ b/cgminer.c @@ -4028,8 +4028,8 @@ static void wait_lpcurrent(struct pool *pool) while (pool != current_pool()) { mutex_lock(&lp_lock); pthread_cond_wait(&lp_cond, &lp_lock); + mutex_unlock(&lp_lock); } - mutex_unlock(&lp_lock); } static void *longpoll_thread(void *userdata) From 4c3e34607f09e61de2da58b93315dbdac5817622 Mon Sep 17 00:00:00 2001 From: Kano Date: Thu, 31 May 2012 20:01:17 +1000 Subject: [PATCH 11/29] Move RPC API content out of README to API-README --- API-README | 447 +++++++++++++++++++++++++++++++++++++++++++++++++++++ README | 276 +-------------------------------- 2 files changed, 448 insertions(+), 275 deletions(-) create mode 100644 API-README diff --git a/API-README b/API-README new file mode 100644 index 00000000..62e6973b --- /dev/null +++ b/API-README @@ -0,0 +1,447 @@ + +This README contains details about the cgminer RPC API + + +If you start cgminer with the "--api-listen" option, it will listen on a +simple TCP/IP socket for single string API requests from the same machine +running cgminer and reply with a string and then close the socket each time +If you add the "--api-network" option, it will accept API requests from any +network attached computer. + +You can only access the comands that reply with data in this mode. +By default, you cannot access any privileged command that affects the miner - +you will receive an access denied status message see --api-allow below. + +You can specify IP addresses/prefixes that are only allowed to access the API +with the "--api-allow" option e.g. --api-allow W:192.168.0.1,10.0.0/24 +will allow 192.168.0.1 or any address matching 10.0.0.*, but nothing else +IP addresses are automatically padded with extra '.0's as needed +Without a /prefix is the same as specifying /32 +0/0 means all IP addresses. +The 'W:' on the front gives that address/subnet privileged access to commands +that modify cgminer. +Without it those commands return an access denied status. +Privileged access is checked in the order the IP addresses were supplied to +"--api-allow" +The first match determines the privilege level. +Using the "--api-allow" option overides the "--api-network" option if they +are both specified +With "--api-allow", 127.0.0.1 is not by default given access unless specified + +The RPC API request can be either simple text or JSON. + +If the request is JSON (starts with '{'), it will reply with a JSON formatted +response, otherwise it replies with text formatted as described further below. + +The JSON request format required is '{"command":"CMD","parameter":"PARAM"}' +(though of course parameter is not required for all requests) +where "CMD" is from the "Request" column below and "PARAM" would be e.g. +the CPU/GPU number if required. + +An example request in both formats to set GPU 0 fan to 80%: + gpufan|0,80 + {"command":"gpufan","parameter":"0,80"} + +The format of each reply (unless stated otherwise) is a STATUS section +followed by an optional detail section + +From API version 1.7 onwards, reply strings in JSON and Text have the +necessary escaping as required to avoid ambiguity - they didn't before 1.7 +For JSON the 2 characters '"' and '\' are escaped with a '\' before them +For Text the 4 characters '|' ',' '=' and '\' are escaped the same way + +Only user entered information will contain characters that require being +escaped, such as Pool URL, User and Password or the Config save filename, +when they are returned in messages or as their values by the API + +For API version 1.4 and later: + +The STATUS section is: + + STATUS=X,When=NNN,Code=N,Msg=string,Description=string| + + STATUS=X Where X is one of: + W - Warning + I - Informational + S - Success + E - Error + F - Fatal (code bug) + + When=NNN + Standard long time of request in seconds + + Code=N + Each unique reply has a unigue Code (See api.c - #define MSG_NNNNNN) + + Msg=string + Message matching the Code value N + + Description=string + This defaults to the cgminer version but is the value of --api-description + if it was specified at runtime. + +For API version 1.9: + +The list of requests - a (*) means it requires privileged access - and replies are: + + Request Reply Section Details + ------- ------------- ------- + version VERSION CGMiner=cgminer version + API=API version + + config CONFIG Some miner configuration information: + GPU Count=N, <- the number of GPUs + PGA Count=N, <- the number of PGAs + CPU Count=N, <- the number of CPUs + Pool Count=N, <- the number of Pools + ADL=X, <- Y or N if ADL is compiled in the code + ADL in use=X, <- Y or N if any GPU has ADL + Strategy=Name, <- the current pool strategy + Log Interval=N, <- log interval (--log N) + Device Code=GPU ICA | <- spaced list of compiled devices + + summary SUMMARY The status summary of the miner + e.g. Elapsed=NNN,Found Blocks=N,Getworks=N,...| + + pools POOLS The status of each pool + e.g. Pool=0,URL=http://pool.com:6311,Status=Alive,...| + + devs DEVS Each available GPU, PGA and CPU with their details + e.g. GPU=0,Accepted=NN,MHS av=NNN,...,Intensity=D| + Last Share Time=NNN, <- standand long time in seconds + (or 0 if none) of last accepted share + Last Share Pool=N, <- pool number (or -1 if none) + Will not report PGAs if PGA mining is disabled + Will not report CPUs if CPU mining is disabled + + gpu|N GPU The details of a single GPU number N in the same + format and details as for DEVS + + pga|N PGA The details of a single PGA number N in the same + format and details as for DEVS + This is only available if PGA mining is enabled + Use 'pgacount' or 'config' first to see if there are any + + cpu|N CPU The details of a single CPU number N in the same + format and details as for DEVS + This is only available if CPU mining is enabled + Use 'cpucount' or 'config' first to see if there are any + + gpucount GPUS Count=N| <- the number of GPUs + + pgacount PGAS Count=N| <- the number of PGAs + Always returns 0 if PGA mining is disabled + + cpucount CPUS Count=N| <- the number of CPUs + Always returns 0 if CPU mining is disabled + + switchpool|N (*) + none There is no reply section just the STATUS section + stating the results of switching pool N to the + highest priority (the pool is also enabled) + The Msg includes the pool URL + + enablepool|N (*) + none There is no reply section just the STATUS section + stating the results of enabling pool N + The Msg includes the pool URL + + addpool|URL,USR,PASS (*) + none There is no reply section just the STATUS section + stating the results of attempting to add pool N + The Msg includes the pool URL + Use '\\' to get a '\' and '\,' to include a comma + inside URL, USR or PASS + + disablepool|N (*) + none There is no reply section just the STATUS section + stating the results of disabling pool N + The Msg includes the pool URL + + removepool|N (*) + none There is no reply section just the STATUS section + stating the results of removing pool N + The Msg includes the pool URL + N.B. all details for the pool will be lost + + gpuenable|N (*) + none There is no reply section just the STATUS section + stating the results of the enable request + + gpudisable|N (*) + none There is no reply section just the STATUS section + stating the results of the disable request + + gpurestart|N (*) + none There is no reply section just the STATUS section + stating the results of the restart request + + gpuintensity|N,I (*) + none There is no reply section just the STATUS section + stating the results of setting GPU N intensity to I + + gpumem|N,V (*) + none There is no reply section just the STATUS section + stating the results of setting GPU N memoryclock to V MHz + + gpuengine|N,V (*) + none There is no reply section just the STATUS section + stating the results of setting GPU N clock to V MHz + + gpufan|N,V (*) + none There is no reply section just the STATUS section + stating the results of setting GPU N fan speed to V% + + gpuvddc|N,V (*) + none There is no reply section just the STATUS section + stating the results of setting GPU N vddc to V + + save|filename (*) + none There is no reply section just the STATUS section + stating success or failure saving the cgminer config + to filename + + quit (*) none There is no status section but just a single "BYE" + reply before cgminer quits + + notify NOTIFY The last status and history count of each devices problem + This lists all devices including those not supported + by the 'devs' command + e.g. NOTIFY=0,Name=GPU,ID=0,Last Well=1332432290,...| + + privileged (*) + none There is no reply section just the STATUS section + stating an error if you do not have privileged access + to the API and success if you do have privilege + The command doesn't change anything in cgminer + + pgaenable|N (*) + none There is no reply section just the STATUS section + stating the results of the enable request + You cannot enable a PGA if it's status is not WELL + This is only available if PGA mining is enabled + + pgadisable|N (*) + none There is no reply section just the STATUS section + stating the results of the disable request + This is only available if PGA mining is enabled + + devdetails DEVDETAILS Each device with a list of their static details + This lists all devices including those not supported + by the 'devs' command + e.g. DEVDETAILS=0,Name=GPU,ID=0,Driver=opencl,...| + + restart (*) none There is no status section but just a single "RESTART" + reply before cgminer restarts + + stats STATS Each device or pool that has 1 or more getworks + with a list of stats regarding getwork times + The values returned by stats may change in future + versions thus would not normally be displayed + Device drivers are also able to add stats to the + end of the details returned + +When you enable, disable or restart a GPU or PGA, you will also get Thread messages +in the cgminer status window + +When you switch to a different pool to the current one, you will get a +'Switching to URL' message in the cgminer status windows + +Obviously, the JSON format is simply just the names as given before the '=' +with the values after the '=' + +If you enable cgminer debug (-D or --debug) you will also get messages showing +details of the requests received and the replies + +There are included 4 program examples for accessing the API: + +api-example.php - a php script to access the API + usAge: php api-example.php command + by default it sends a 'summary' request to the miner at 127.0.0.1:4028 + If you specify a command it will send that request instead + You must modify the line "$socket = getsock('127.0.0.1', 4028);" at the + beginning of "function request($cmd)" to change where it looks for cgminer + +API.java/API.class + a java program to access the API (with source code) + usAge is: java API command address port + Any missing or blank parameters are replaced as if you entered: + java API summary 127.0.0.1 4028 + +api-example.c - a 'C' program to access the API (with source code) + usAge: api-example [command [ip/host [port]]] + again, as above, missing or blank parameters are replaced as if you entered: + api-example summary 127.0.0.1 4028 + +miner.php - an example web page to access the API + This includes buttons and inputs to attempt access to the privileged commands + Read the top of the file (miner.php) for details of how to tune the display + and also to use the option to display a multi-rig summary + +---------- + +Feature Changelog for external applications using the API: + + +API V1.10 + +Added API commands: + 'stats' + +N.B. the 'stats' command can change at any time so any specific content +present should not be relied upon. +The data content is mainly used for debugging purposes or hidden options +in cgminer and can change as development work requires + +Modified API commands: + 'pools' added "Last Share Time" + +---------- + +API V1.9 (cgminer v2.4.0) + +Added API commands: + 'restart' + +Modified API commands: + 'notify' corrected invalid JSON + +---------- + +API V1.8 (cgminer v2.3.5) + +Added API commands: + 'devdetails' + +Support for the ZTex FPGA was added + +---------- + +API V1.7 (cgminer v2.3.4) + +Added API commands: + 'removepool' + +Modified API commands: + 'pools' added "User" + +From API version 1.7 onwards, reply strings in JSON and Text have the +necessary escaping as required to avoid ambiguity +For JSON the 2 characters '"' and '\' are escaped with a '\' before them +For Text the 4 characters '|' ',' '=' and '\' are escaped the same way + +---------- + +API V1.6 (cgminer v2.3.2) + +Added API commands: + 'pga' + 'pgaenable' + 'pgadisable' + 'pgacount' + +Modified API commands: + 'devs' now includes Icarus and Bitforce FPGA devices + 'notify' added "*" to the front of the name of all numeric error fields + 'config' correct "Log Interval" to use numeric (not text) type for JSON + +Support for Icarus and Bitforce FPGAs was added + +---------- + +API V1.5 (Kano's interim release of cgminer v2.3.1) + +Modified API commands: + 'config' added "Device Code" and "OS" + +---------- + +API V1.4 (Kano's interim release of cgminer v2.3.1) + +Added API commands: + 'notify' + +Added "When" to the STATUS reply section of all commands + +---------- + +API V1.3 (cgminer v2.3.1-2) + +Added API commands: + 'addpool' + +Modified API commands: + 'devs'/'gpu' added "Total MH" for each device + 'summary' added "Total MH" + +---------- + +API V1.2 (cgminer v2.3.0) + +Added API commands: + 'enablepool' + 'disablepool' + 'privileged' + +Modified API commands: + 'config' added "Log Interval" + +Starting with API V1.2, any attempt to access a command that requires +privileged security, from an IP address that does not have privileged +security, will return an "Access denied" Error Status + +---------- + +API V1.1 (cgminer v2.2.4) + +There were no changes to the API commands in cgminer v2.2.4, +however support was added to cgminer for IP address restrictions +with the --api-allow option + +---------- + +API V1.1 (cgminer v2.2.2) + +Prior to V1.1, devs/gpu incorrectly reported GPU0 Intensity for all GPUs + +Modified API commands: + 'devs'/'gpu' added "Last Share Pool" and "Last Share Time" for each device + +---------- + +API V1.0 (cgminer v2.2.0) + +Remove default CPU support + +Added API commands: + 'config' + 'gpucount' + 'cpucount' + 'switchpool' + 'gpuintensity' + 'gpumem' + 'gpuengine' + 'gpufan' + 'gpuvddc' + 'save' + +---------- + +API V0.7 (cgminer v2.1.0) + +Initial release of the API in the main cgminer git + +Commands: + 'version' + 'devs' + 'pools' + 'summary' + 'gpuenable' + 'gpudisable' + 'gpurestart' + 'gpu' + 'cpu' + 'gpucount' + 'cpucount' + 'quit' + diff --git a/README b/README index 9c445e84..af78af1e 100644 --- a/README +++ b/README @@ -571,281 +571,7 @@ cgminer shuts down because of this. RPC API -If you start cgminer with the "--api-listen" option, it will listen on a -simple TCP/IP socket for single string API requests from the same machine -running cgminer and reply with a string and then close the socket each time -If you add the "--api-network" option, it will accept API requests from any -network attached computer. - -You can only access the comands that reply with data in this mode. -By default, you cannot access any privileged command that affects the miner - -you will receive an access denied status message see --api-allow below. - -You can specify IP addresses/prefixes that are only allowed to access the API -with the "--api-allow" option e.g. --api-allow W:192.168.0.1,10.0.0/24 -will allow 192.168.0.1 or any address matching 10.0.0.*, but nothing else -IP addresses are automatically padded with extra '.0's as needed -Without a /prefix is the same as specifying /32 -0/0 means all IP addresses. -The 'W:' on the front gives that address/subnet privileged access to commands -that modify cgminer. -Without it those commands return an access denied status. -Privileged access is checked in the order the IP addresses were supplied to -"--api-allow" -The first match determines the privilege level. -Using the "--api-allow" option overides the "--api-network" option if they -are both specified -With "--api-allow", 127.0.0.1 is not by default given access unless specified - -The RPC API request can be either simple text or JSON. - -If the request is JSON (starts with '{'), it will reply with a JSON formatted -response, otherwise it replies with text formatted as described further below. - -The JSON request format required is '{"command":"CMD","parameter":"PARAM"}' -(though of course parameter is not required for all requests) -where "CMD" is from the "Request" column below and "PARAM" would be e.g. -the CPU/GPU number if required. - -An example request in both formats to set GPU 0 fan to 80%: - gpufan|0,80 - {"command":"gpufan","parameter":"0,80"} - -The format of each reply (unless stated otherwise) is a STATUS section -followed by an optional detail section - -From API version 1.7 onwards, reply strings in JSON and Text have the -necessary escaping as required to avoid ambiguity - they didn't before 1.7 -For JSON the 2 characters '"' and '\' are escaped with a '\' before them -For Text the 4 characters '|' ',' '=' and '\' are escaped the same way - -Only user entered information will contain characters that require being -escaped, such as Pool URL, User and Password or the Config save filename, -when they are returned in messages or as their values by the API - -For API version 1.4 and later: - -The STATUS section is: - - STATUS=X,When=NNN,Code=N,Msg=string,Description=string| - - STATUS=X Where X is one of: - W - Warning - I - Informational - S - Success - E - Error - F - Fatal (code bug) - - When=NNN - Standard long time of request in seconds - - Code=N - Each unique reply has a unigue Code (See api.c - #define MSG_NNNNNN) - - Msg=string - Message matching the Code value N - - Description=string - This defaults to the cgminer version but is the value of --api-description - if it was specified at runtime. - -For API version 1.9: - -The list of requests - a (*) means it requires privileged access - and replies are: - - Request Reply Section Details - ------- ------------- ------- - version VERSION CGMiner=cgminer version - API=API version - - config CONFIG Some miner configuration information: - GPU Count=N, <- the number of GPUs - PGA Count=N, <- the number of PGAs - CPU Count=N, <- the number of CPUs - Pool Count=N, <- the number of Pools - ADL=X, <- Y or N if ADL is compiled in the code - ADL in use=X, <- Y or N if any GPU has ADL - Strategy=Name, <- the current pool strategy - Log Interval=N, <- log interval (--log N) - Device Code=GPU ICA | <- spaced list of compiled devices - - summary SUMMARY The status summary of the miner - e.g. Elapsed=NNN,Found Blocks=N,Getworks=N,...| - - pools POOLS The status of each pool - e.g. Pool=0,URL=http://pool.com:6311,Status=Alive,...| - - devs DEVS Each available GPU, PGA and CPU with their details - e.g. GPU=0,Accepted=NN,MHS av=NNN,...,Intensity=D| - Last Share Time=NNN, <- standand long time in seconds - (or 0 if none) of last accepted share - Last Share Pool=N, <- pool number (or -1 if none) - Will not report PGAs if PGA mining is disabled - Will not report CPUs if CPU mining is disabled - - gpu|N GPU The details of a single GPU number N in the same - format and details as for DEVS - - pga|N PGA The details of a single PGA number N in the same - format and details as for DEVS - This is only available if PGA mining is enabled - Use 'pgacount' or 'config' first to see if there are any - - cpu|N CPU The details of a single CPU number N in the same - format and details as for DEVS - This is only available if CPU mining is enabled - Use 'cpucount' or 'config' first to see if there are any - - gpucount GPUS Count=N| <- the number of GPUs - - pgacount PGAS Count=N| <- the number of PGAs - Always returns 0 if PGA mining is disabled - - cpucount CPUS Count=N| <- the number of CPUs - Always returns 0 if CPU mining is disabled - - switchpool|N (*) - none There is no reply section just the STATUS section - stating the results of switching pool N to the - highest priority (the pool is also enabled) - The Msg includes the pool URL - - enablepool|N (*) - none There is no reply section just the STATUS section - stating the results of enabling pool N - The Msg includes the pool URL - - addpool|URL,USR,PASS (*) - none There is no reply section just the STATUS section - stating the results of attempting to add pool N - The Msg includes the pool URL - Use '\\' to get a '\' and '\,' to include a comma - inside URL, USR or PASS - - disablepool|N (*) - none There is no reply section just the STATUS section - stating the results of disabling pool N - The Msg includes the pool URL - - removepool|N (*) - none There is no reply section just the STATUS section - stating the results of removing pool N - The Msg includes the pool URL - N.B. all details for the pool will be lost - - gpuenable|N (*) - none There is no reply section just the STATUS section - stating the results of the enable request - - gpudisable|N (*) - none There is no reply section just the STATUS section - stating the results of the disable request - - gpurestart|N (*) - none There is no reply section just the STATUS section - stating the results of the restart request - - gpuintensity|N,I (*) - none There is no reply section just the STATUS section - stating the results of setting GPU N intensity to I - - gpumem|N,V (*) - none There is no reply section just the STATUS section - stating the results of setting GPU N memoryclock to V MHz - - gpuengine|N,V (*) - none There is no reply section just the STATUS section - stating the results of setting GPU N clock to V MHz - - gpufan|N,V (*) - none There is no reply section just the STATUS section - stating the results of setting GPU N fan speed to V% - - gpuvddc|N,V (*) - none There is no reply section just the STATUS section - stating the results of setting GPU N vddc to V - - save|filename (*) - none There is no reply section just the STATUS section - stating success or failure saving the cgminer config - to filename - - quit (*) none There is no status section but just a single "BYE" - reply before cgminer quits - - notify NOTIFY The last status and history count of each devices problem - This lists all devices including those not supported - by the 'devs' command - e.g. NOTIFY=0,Name=GPU,ID=0,Last Well=1332432290,...| - - privileged (*) - none There is no reply section just the STATUS section - stating an error if you do not have privileged access - to the API and success if you do have privilege - The command doesn't change anything in cgminer - - pgaenable|N (*) - none There is no reply section just the STATUS section - stating the results of the enable request - You cannot enable a PGA if it's status is not WELL - This is only available if PGA mining is enabled - - pgadisable|N (*) - none There is no reply section just the STATUS section - stating the results of the disable request - This is only available if PGA mining is enabled - - devdetails DEVDETAILS Each device with a list of their static details - This lists all devices including those not supported - by the 'devs' command - e.g. DEVDETAILS=0,Name=GPU,ID=0,Driver=opencl,...| - - restart (*) none There is no status section but just a single "RESTART" - reply before cgminer restarts - - stats STATS Each device or pool that has 1 or more getworks - with a list of stats regarding getwork times - The values returned by stats may change in future - versions thus would not normally be displayed - Device drivers are also able to add stats to the - end of the details returned - -When you enable, disable or restart a GPU or PGA, you will also get Thread messages -in the cgminer status window - -When you switch to a different pool to the current one, you will get a -'Switching to URL' message in the cgminer status windows - -Obviously, the JSON format is simply just the names as given before the '=' -with the values after the '=' - -If you enable cgminer debug (-D or --debug) you will also get messages showing -details of the requests received and the replies - -There are included 4 program examples for accessing the API: - -api-example.php - a php script to access the API - usAge: php api-example.php command - by default it sends a 'summary' request to the miner at 127.0.0.1:4028 - If you specify a command it will send that request instead - You must modify the line "$socket = getsock('127.0.0.1', 4028);" at the - beginning of "function request($cmd)" to change where it looks for cgminer - -API.java/API.class - a java program to access the API (with source code) - usAge is: java API command address port - Any missing or blank parameters are replaced as if you entered: - java API summary 127.0.0.1 4028 - -api-example.c - a 'C' program to access the API (with source code) - usAge: api-example [command [ip/host [port]]] - again, as above, missing or blank parameters are replaced as if you entered: - api-example summary 127.0.0.1 4028 - -miner.php - an example web page to access the API - This includes buttons and inputs to attempt access to the privileged commands - Read the top of the file (miner.php) for details of how to tune the display - and also to use the option to display a multi-rig summary +For RPC API details see the API-README file --- From 1de364f95b909aa37443978a54ab3b234e3c62cf Mon Sep 17 00:00:00 2001 From: Kano Date: Thu, 31 May 2012 20:18:04 +1000 Subject: [PATCH 12/29] API-README no one actually had a V1.5 --- API-README | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/API-README b/API-README index 62e6973b..706ffb01 100644 --- a/API-README +++ b/API-README @@ -349,10 +349,7 @@ Support for Icarus and Bitforce FPGAs was added ---------- -API V1.5 (Kano's interim release of cgminer v2.3.1) - -Modified API commands: - 'config' added "Device Code" and "OS" +API V1.5 was not released ---------- @@ -361,6 +358,9 @@ API V1.4 (Kano's interim release of cgminer v2.3.1) Added API commands: 'notify' +Modified API commands: + 'config' added "Device Code" and "OS" + Added "When" to the STATUS reply section of all commands ---------- From a0936815a39dc67b1033c705a4c1ba9fe48651fa Mon Sep 17 00:00:00 2001 From: Kano Date: Thu, 31 May 2012 22:41:04 +1000 Subject: [PATCH 13/29] api.c replace BUFSIZ (linux/windows have different values) --- api.c | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/api.c b/api.c index a0c2e560..731c4e1f 100644 --- a/api.c +++ b/api.c @@ -142,6 +142,9 @@ // Current code assumes it can socket send this size also #define MYBUFSIZ 65432 // TODO: intercept before it's exceeded +// BUFSIZ varies on Windows and Linux +#define TMPBUFSIZ 8192 + // Number of requests to queue - normally would be small // However lots of PGA's may mean more #define QUEUE 100 @@ -763,7 +766,7 @@ static void apiversion(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, static void minerconfig(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson) { - char buf[BUFSIZ]; + char buf[TMPBUFSIZ]; int pgacount = 0; int cpucount = 0; char *adlinuse = (char *)NO; @@ -804,7 +807,7 @@ static void minerconfig(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, static void gpustatus(int gpu, bool isjson) { char intensity[20]; - char buf[BUFSIZ]; + char buf[TMPBUFSIZ]; char *enabled; char *status; float gt, gv; @@ -856,7 +859,7 @@ static void gpustatus(int gpu, bool isjson) #if defined(USE_BITFORCE) || defined(USE_ICARUS) || defined(USE_ZTEX) static void pgastatus(int pga, bool isjson) { - char buf[BUFSIZ]; + char buf[TMPBUFSIZ]; char *enabled; char *status; int numpga = numpgas(); @@ -908,7 +911,7 @@ static void pgastatus(int pga, bool isjson) #ifdef WANT_CPUMINE static void cpustatus(int cpu, bool isjson) { - char buf[BUFSIZ]; + char buf[TMPBUFSIZ]; if (opt_n_threads > 0 && cpu >= 0 && cpu < num_processors) { struct cgpu_info *cgpu = &cpus[cpu]; @@ -1188,7 +1191,7 @@ static void cpudev(__maybe_unused SOCKETTYPE c, char *param, bool isjson) static void poolstatus(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson) { - char buf[BUFSIZ]; + char buf[TMPBUFSIZ]; char *status, *lp; char *rpc_url; char *rpc_user; @@ -1400,7 +1403,7 @@ static void gpurestart(__maybe_unused SOCKETTYPE c, char *param, bool isjson) static void gpucount(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson) { - char buf[BUFSIZ]; + char buf[TMPBUFSIZ]; strcpy(io_buffer, message(MSG_NUMGPU, 0, NULL, isjson)); @@ -1414,7 +1417,7 @@ static void gpucount(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bo static void pgacount(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson) { - char buf[BUFSIZ]; + char buf[TMPBUFSIZ]; int count = 0; #if defined(USE_BITFORCE) || defined(USE_ICARUS) || defined(USE_ZTEX) @@ -1433,7 +1436,7 @@ static void pgacount(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bo static void cpucount(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson) { - char buf[BUFSIZ]; + char buf[TMPBUFSIZ]; int count = 0; #ifdef WANT_CPUMINE @@ -1863,7 +1866,7 @@ void privileged(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool is void notifystatus(int device, struct cgpu_info *cgpu, bool isjson) { - char buf[BUFSIZ]; + char buf[TMPBUFSIZ]; char *reason; if (cgpu->device_last_not_well == 0) @@ -1940,7 +1943,7 @@ static void notify(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool static void devdetails(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson) { - char buf[BUFSIZ]; + char buf[TMPBUFSIZ]; struct cgpu_info *cgpu; int i; @@ -2006,7 +2009,7 @@ void dosave(__maybe_unused SOCKETTYPE c, char *param, bool isjson) static int itemstats(int i, char *id, struct cgminer_stats *stats, char *extra, bool isjson) { - char buf[BUFSIZ]; + char buf[TMPBUFSIZ]; if (stats->getwork_calls || (extra != NULL && *extra)) { @@ -2032,7 +2035,7 @@ static int itemstats(int i, char *id, struct cgminer_stats *stats, char *extra, } static void minerstats(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson) { - char extra[BUFSIZ]; + char extra[TMPBUFSIZ]; char id[20]; int i, j; @@ -2300,8 +2303,8 @@ static void *restart_thread(__maybe_unused void *userdata) void api(int api_thr_id) { struct thr_info bye_thr; - char buf[BUFSIZ]; - char param_buf[BUFSIZ]; + char buf[TMPBUFSIZ]; + char param_buf[TMPBUFSIZ]; const char *localaddr = "127.0.0.1"; SOCKETTYPE c; int n, bound; @@ -2434,7 +2437,7 @@ void api(int api_thr_id) applog(LOG_DEBUG, "API: connection from %s - %s", connectaddr, addrok ? "Accepted" : "Ignored"); if (addrok) { - n = recv(c, &buf[0], BUFSIZ-1, 0); + n = recv(c, &buf[0], TMPBUFSIZ-1, 0); if (SOCKETFAIL(n)) buf[0] = '\0'; else From fd9285eb72332e179d06294d9be28ab6ef4cce21 Mon Sep 17 00:00:00 2001 From: Kano Date: Thu, 31 May 2012 22:46:43 +1000 Subject: [PATCH 14/29] api.c save success incorrectly returns error --- api.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api.c b/api.c index a0c2e560..419ae6f1 100644 --- a/api.c +++ b/api.c @@ -462,7 +462,7 @@ struct CODES { { SEVERITY_SUCC, MSG_GPUFAN, PARAM_BOTH, "Setting GPU %d fan to (%s) reported succeess" }, { SEVERITY_ERR, MSG_MISFN, PARAM_NONE, "Missing save filename parameter" }, { SEVERITY_ERR, MSG_BADFN, PARAM_STR, "Can't open or create save file '%s'" }, - { SEVERITY_ERR, MSG_SAVED, PARAM_STR, "Configuration saved to file '%s'" }, + { SEVERITY_SUCC, MSG_SAVED, PARAM_STR, "Configuration saved to file '%s'" }, { SEVERITY_ERR, MSG_ACCDENY, PARAM_STR, "Access denied to '%s' command" }, { SEVERITY_SUCC, MSG_ACCOK, PARAM_NONE, "Privileged access OK" }, { SEVERITY_SUCC, MSG_ENAPOOL, PARAM_POOL, "Enabling pool %d:'%s'" }, From 3cb3f09a505327abba4a5f1a9b1366c683cc1bef Mon Sep 17 00:00:00 2001 From: Kano Date: Thu, 31 May 2012 23:18:55 +1000 Subject: [PATCH 15/29] API save default config file if none specified --- api.c | 5 +++-- cgminer.c | 31 ++++++++++++++++++------------- miner.h | 1 + 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/api.c b/api.c index 419ae6f1..0bc65e28 100644 --- a/api.c +++ b/api.c @@ -1976,12 +1976,13 @@ static void devdetails(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, void dosave(__maybe_unused SOCKETTYPE c, char *param, bool isjson) { + char filename[PATH_MAX]; FILE *fcfg; char *ptr; if (param == NULL || *param == '\0') { - strcpy(io_buffer, message(MSG_MISFN, 0, NULL, isjson)); - return; + default_save_file(filename); + param = filename; } fcfg = fopen(param, "w"); diff --git a/cgminer.c b/cgminer.c index 9565fc1b..cb4f6f26 100644 --- a/cgminer.c +++ b/cgminer.c @@ -3006,6 +3006,23 @@ retry: } #endif +void default_save_file(char *filename) +{ +#if defined(unix) + if (getenv("HOME") && *getenv("HOME")) { + strcpy(filename, getenv("HOME")); + strcat(filename, "/"); + } + else + strcpy(filename, ""); + strcat(filename, ".cgminer/"); + mkdir(filename, 0777); +#else + strcpy(filename, ""); +#endif + strcat(filename, def_conf); +} + #ifdef HAVE_CURSES static void set_options(void) { @@ -3066,19 +3083,7 @@ retry: FILE *fcfg; char *str, filename[PATH_MAX], prompt[PATH_MAX + 50]; -#if defined(unix) - if (getenv("HOME") && *getenv("HOME")) { - strcpy(filename, getenv("HOME")); - strcat(filename, "/"); - } - else - strcpy(filename, ""); - strcat(filename, ".cgminer/"); - mkdir(filename, 0777); -#else - strcpy(filename, ""); -#endif - strcat(filename, def_conf); + default_save_file(filename); sprintf(prompt, "Config filename to write (Enter for default) [%s]", filename); str = curses_input(prompt); if (strcmp(str, "-1")) { diff --git a/miner.h b/miner.h index 4e2f752d..ec9d725b 100644 --- a/miner.h +++ b/miner.h @@ -742,6 +742,7 @@ extern void kill_work(void); extern void switch_pools(struct pool *selected); extern void remove_pool(struct pool *pool); extern void write_config(FILE *fcfg); +extern void default_save_file(char *filename); extern void log_curses(int prio, const char *f, va_list ap); extern void clear_logwin(void); extern bool pool_tclear(struct pool *pool, bool *var); From 977aecc4a60928d05719e3b0b534335b1f82effa Mon Sep 17 00:00:00 2001 From: Kano Date: Thu, 31 May 2012 23:30:38 +1000 Subject: [PATCH 16/29] API update version to V1.11 and document changes --- API-README | 14 +++++++++++++- api.c | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/API-README b/API-README index 706ffb01..07824576 100644 --- a/API-README +++ b/API-README @@ -200,6 +200,8 @@ The list of requests - a (*) means it requires privileged access - and replies a none There is no reply section just the STATUS section stating success or failure saving the cgminer config to filename + The filename is optional and will use the cgminer + default if not specified quit (*) none There is no status section but just a single "BYE" reply before cgminer quits @@ -283,7 +285,17 @@ miner.php - an example web page to access the API Feature Changelog for external applications using the API: -API V1.10 +API V1.11 + +Modified API commands: + 'save' no longer requires a filename (use default if not specified) + +'save' incorrectly returned status E (error) on success before. +It now correctly returns S (success) + +---------- + +API V1.10 (cgminer v2.4.1) Added API commands: 'stats' diff --git a/api.c b/api.c index 0bc65e28..94c5239d 100644 --- a/api.c +++ b/api.c @@ -158,7 +158,7 @@ static const char SEPARATOR = '|'; #define SEPSTR "|" static const char GPUSEP = ','; -static const char *APIVERSION = "1.10"; +static const char *APIVERSION = "1.11"; static const char *DEAD = "Dead"; static const char *SICK = "Sick"; static const char *NOSTART = "NoStart"; From 9cd010782f1d9a87ce2c3bda441aefae851edf19 Mon Sep 17 00:00:00 2001 From: Kano Date: Sat, 2 Jun 2012 01:25:27 +1000 Subject: [PATCH 17/29] miner.php highlight devs too slow finding shares (possibly failing) --- miner.php | 339 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 224 insertions(+), 115 deletions(-) diff --git a/miner.php b/miner.php index 2441cad8..3372d391 100644 --- a/miner.php +++ b/miner.php @@ -2,6 +2,7 @@ session_start(); # global $miner, $port, $readonly, $notify, $rigs, $socktimeoutsec; +global $checklastshare; # # Don't touch these 2 - see $rigs below $miner = null; @@ -17,6 +18,13 @@ $readonly = false; # coz it doesn't have notify - it just shows the error status table $notify = true; # +# set $checklastshare to true to do the following checks: +# If a device's last share is 12x expected ago then display as an error +# If a device's last share is 8x expected ago then display as a warning +# If either of the above is true, also display the whole line highlighted +# This assumes shares are 1 difficulty shares +$checklastshare = true; +# # Set $rigs to an array of your cgminer rigs that are running # format: 'IP:Port' or 'Host:Port' # If you only have one rig, it will just show the detail of that rig @@ -70,6 +78,7 @@ td.err { color:black; font-family:verdana,arial,sans; font-size:13pt; background td.warn { color:black; font-family:verdana,arial,sans; font-size:13pt; background:#ffb050 } td.sta { color:green; font-family:verdana,arial,sans; font-size:13pt; } td.tot { color:blue; font-family:verdana,arial,sans; font-size:13pt; background:#fff8f2 } +td.lst { color:blue; font-family:verdana,arial,sans; font-size:13pt; background:#ffffdd }