Browse Source

Merge branch 'port-ckolivas-3.12.3'

Could be issue 88 (no reconnect to pools on network connectivity going
down for a while and then back up) is a regression introduced in pool
handling after cgminer 3.7.2. Pull in other changes to pool handling
just in case.

Tests show that this may be an issue with glibc's getaddrinfo(), and that
the last changes in cgminer might mitigate the issue somewhat.
nfactor-troky
Noel Maersk 11 years ago
parent
commit
80e6307033
  1. 2
      NEWS.md
  2. 223
      api.c
  3. 38
      doc/API
  4. 9
      miner.h
  5. 115
      sgminer.c
  6. 35
      util.c
  7. 14
      util.h

2
NEWS.md

@ -2,7 +2,7 @@
## Version 4.2.0 - TBA ## Version 4.2.0 - TBA
* Forward-port changes from `ckolivas/cgminer` up to 3.11.0. * Forward-port changes from `ckolivas/cgminer` up to 3.12.3.
## Version 4.1.0 - 7th February 2014 ## Version 4.1.0 - 7th February 2014

223
api.c

@ -29,12 +29,6 @@
#include "miner.h" #include "miner.h"
#include "util.h" #include "util.h"
// Big enough for largest API request
// data is truncated at the end of the last record that fits
// but still closed correctly for JSON
// Current code assumes it can socket send this size + JSON_CLOSE + JSON_END
#define SOCKBUFSIZ 65432
// BUFSIZ varies on Windows and Linux // BUFSIZ varies on Windows and Linux
#define TMPBUFSIZ 8192 #define TMPBUFSIZ 8192
@ -126,7 +120,11 @@ static const char SEPARATOR = '|';
#define SEPSTR "|" #define SEPSTR "|"
static const char GPUSEP = ','; static const char GPUSEP = ',';
static const char *APIVERSION = "3.0"; #define CMDJOIN '+'
#define JOIN_CMD "CMD="
#define BETWEEN_JOIN SEPSTR
static const char *APIVERSION = "3.1";
static const char *DEAD = "Dead"; static const char *DEAD = "Dead";
static const char *SICK = "Sick"; static const char *SICK = "Sick";
static const char *NOSTART = "NoStart"; static const char *NOSTART = "NoStart";
@ -215,9 +213,10 @@ static const char ISJSON = '{';
#define JSON_MINECOIN JSON1 _MINECOIN JSON2 #define JSON_MINECOIN JSON1 _MINECOIN JSON2
#define JSON_DEBUGSET JSON1 _DEBUGSET JSON2 #define JSON_DEBUGSET JSON1 _DEBUGSET JSON2
#define JSON_SETCONFIG JSON1 _SETCONFIG JSON2 #define JSON_SETCONFIG JSON1 _SETCONFIG JSON2
#define JSON_USBSTATS JSON1 _USBSTATS JSON2
#define JSON_END JSON4 JSON5 #define JSON_END JSON4 JSON5
#define JSON_END_TRUNCATED JSON4_TRUNCATED JSON5 #define JSON_END_TRUNCATED JSON4_TRUNCATED JSON5
#define JSON_BETWEEN_JOIN ","
static const char *JSON_COMMAND = "command"; static const char *JSON_COMMAND = "command";
static const char *JSON_PARAMETER = "parameter"; static const char *JSON_PARAMETER = "parameter";
@ -296,7 +295,7 @@ static const char *JSON_PARAMETER = "parameter";
#define MSG_INVNUM 84 #define MSG_INVNUM 84
#define MSG_CONPAR 85 #define MSG_CONPAR 85
#define MSG_CONVAL 86 #define MSG_CONVAL 86
#define MSG_USBSTA 87
#define MSG_NOUSTA 88 #define MSG_NOUSTA 88
#define MSG_ZERMIS 94 #define MSG_ZERMIS 94
@ -418,7 +417,6 @@ struct CODES {
{ SEVERITY_SUCC, MSG_SETQUOTA,PARAM_SET, "Set pool '%s' to quota %d'" }, { SEVERITY_SUCC, MSG_SETQUOTA,PARAM_SET, "Set pool '%s' to quota %d'" },
{ SEVERITY_ERR, MSG_CONPAR, PARAM_NONE, "Missing config parameters 'name,N'" }, { SEVERITY_ERR, MSG_CONPAR, PARAM_NONE, "Missing config parameters 'name,N'" },
{ SEVERITY_ERR, MSG_CONVAL, PARAM_STR, "Missing config value N for '%s,N'" }, { SEVERITY_ERR, MSG_CONVAL, PARAM_STR, "Missing config value N for '%s,N'" },
{ SEVERITY_SUCC, MSG_USBSTA, PARAM_NONE, "USB Statistics" },
{ SEVERITY_INFO, MSG_NOUSTA, PARAM_NONE, "No USB Statistics" }, { SEVERITY_INFO, MSG_NOUSTA, PARAM_NONE, "No USB Statistics" },
{ SEVERITY_ERR, MSG_ZERMIS, PARAM_NONE, "Missing zero parameters" }, { SEVERITY_ERR, MSG_ZERMIS, PARAM_NONE, "Missing zero parameters" },
{ SEVERITY_ERR, MSG_ZERINV, PARAM_STR, "Invalid zero parameter '%s'" }, { SEVERITY_ERR, MSG_ZERINV, PARAM_STR, "Invalid zero parameter '%s'" },
@ -729,6 +727,10 @@ static struct api_data *api_add_data_full(struct api_data *root, char *name, enu
api_data->data = (void *)malloc(sizeof(uint32_t)); api_data->data = (void *)malloc(sizeof(uint32_t));
*((uint32_t *)(api_data->data)) = *((uint32_t *)data); *((uint32_t *)(api_data->data)) = *((uint32_t *)data);
break; break;
case API_HEX32:
api_data->data = (void *)malloc(sizeof(uint32_t));
*((uint32_t *)(api_data->data)) = *((uint32_t *)data);
break;
case API_UINT64: case API_UINT64:
api_data->data = (void *)malloc(sizeof(uint64_t)); api_data->data = (void *)malloc(sizeof(uint64_t));
*((uint64_t *)(api_data->data)) = *((uint64_t *)data); *((uint64_t *)(api_data->data)) = *((uint64_t *)data);
@ -815,6 +817,11 @@ struct api_data *api_add_uint32(struct api_data *root, char *name, uint32_t *dat
return api_add_data_full(root, name, API_UINT32, (void *)data, copy_data); return api_add_data_full(root, name, API_UINT32, (void *)data, copy_data);
} }
struct api_data *api_add_hex32(struct api_data *root, char *name, uint32_t *data, bool copy_data)
{
return api_add_data_full(root, name, API_HEX32, (void *)data, copy_data);
}
struct api_data *api_add_uint64(struct api_data *root, char *name, uint64_t *data, bool copy_data) struct api_data *api_add_uint64(struct api_data *root, char *name, uint64_t *data, bool copy_data)
{ {
return api_add_data_full(root, name, API_UINT64, (void *)data, copy_data); return api_add_data_full(root, name, API_UINT64, (void *)data, copy_data);
@ -958,6 +965,9 @@ static struct api_data *print_data(struct api_data *root, char *buf, bool isjson
case API_UINT32: case API_UINT32:
sprintf(buf, "%"PRIu32, *((uint32_t *)(root->data))); sprintf(buf, "%"PRIu32, *((uint32_t *)(root->data)));
break; break;
case API_HEX32:
snprintf(buf, sizeof(buf), "0x%08x", *((uint32_t *)(root->data)));
break;
case API_UINT64: case API_UINT64:
sprintf(buf, "%"PRIu64, *((uint64_t *)(root->data))); sprintf(buf, "%"PRIu64, *((uint64_t *)(root->data)));
break; break;
@ -995,9 +1005,9 @@ static struct api_data *print_data(struct api_data *root, char *buf, bool isjson
sprintf(buf, "%s", *((bool *)(root->data)) ? TRUESTR : FALSESTR); sprintf(buf, "%s", *((bool *)(root->data)) ? TRUESTR : FALSESTR);
break; break;
case API_TIMEVAL: case API_TIMEVAL:
sprintf(buf, "%ld.%06ld", snprintf(buf, sizeof(buf), "%ld.%06ld",
((struct timeval *)(root->data))->tv_sec, (long)((struct timeval *)(root->data))->tv_sec,
((struct timeval *)(root->data))->tv_usec); (long)((struct timeval *)(root->data))->tv_usec);
break; break;
case API_TEMP: case API_TEMP:
sprintf(buf, "%.2f", *((float *)(root->data))); sprintf(buf, "%.2f", *((float *)(root->data)));
@ -1049,10 +1059,8 @@ static void message(struct io_data *io_data, int messageid, int paramid, char *p
int i; int i;
io_reinit(io_data);
if (isjson) if (isjson)
io_put(io_data, JSON_START JSON_STATUS); io_add(io_data, JSON_START JSON_STATUS);
for (i = 0; codes[i].severity != SEVERITY_FAIL; i++) { for (i = 0; codes[i].severity != SEVERITY_FAIL; i++) {
if (codes[i].code == messageid) { if (codes[i].code == messageid) {
@ -3033,44 +3041,45 @@ struct CMDS {
char *name; char *name;
void (*func)(struct io_data *, SOCKETTYPE, char *, bool, char); void (*func)(struct io_data *, SOCKETTYPE, char *, bool, char);
bool iswritemode; bool iswritemode;
bool joinable;
} cmds[] = { } cmds[] = {
{ "version", apiversion, false }, { "version", apiversion, false, true },
{ "config", minerconfig, false }, { "config", minerconfig, false, true },
{ "devs", devstatus, false }, { "devs", devstatus, false, true },
{ "pools", poolstatus, false }, { "pools", poolstatus, false, true },
{ "summary", summary, false }, { "summary", summary, false, true },
{ "gpuenable", gpuenable, true }, { "gpuenable", gpuenable, true, false },
{ "gpudisable", gpudisable, true }, { "gpudisable", gpudisable, true, false },
{ "gpurestart", gpurestart, true }, { "gpurestart", gpurestart, true, false },
{ "gpu", gpudev, false }, { "gpu", gpudev, false, false },
{ "gpucount", gpucount, false }, { "gpucount", gpucount, false, true },
{ "switchpool", switchpool, true }, { "switchpool", switchpool, true, false },
{ "addpool", addpool, true }, { "addpool", addpool, true, false },
{ "poolpriority", poolpriority, true }, { "poolpriority", poolpriority, true, false },
{ "poolquota", poolquota, true }, { "poolquota", poolquota, true, false },
{ "enablepool", enablepool, true }, { "enablepool", enablepool, true, false },
{ "disablepool", disablepool, true }, { "disablepool", disablepool, true, false },
{ "removepool", removepool, true }, { "removepool", removepool, true, false },
{ "gpuintensity", gpuintensity, true }, { "gpuintensity", gpuintensity, true, false },
{ "gpumem", gpumem, true }, { "gpumem", gpumem, true, false },
{ "gpuengine", gpuengine, true }, { "gpuengine", gpuengine, true, false },
{ "gpufan", gpufan, true }, { "gpufan", gpufan, true, false },
{ "gpuvddc", gpuvddc, true }, { "gpuvddc", gpuvddc, true, false },
{ "save", dosave, true }, { "save", dosave, true, false },
{ "quit", doquit, true }, { "quit", doquit, true, false },
{ "privileged", privileged, true }, { "privileged", privileged, true, false },
{ "notify", notify, false }, { "notify", notify, false, true },
{ "devdetails", devdetails, false }, { "devdetails", devdetails, false, true },
{ "restart", dorestart, true }, { "restart", dorestart, true, false },
{ "stats", minerstats, false }, { "stats", minerstats, false, true },
{ "check", checkcommand, false }, { "check", checkcommand, false, false },
{ "failover-only", failoveronly, true }, { "failover-only", failoveronly, true, false },
{ "coin", minecoin, false }, { "coin", minecoin, false, true },
{ "debug", debugstate, true }, { "debug", debugstate, true, false },
{ "setconfig", setconfig, true }, { "setconfig", setconfig, true, false },
{ "zero", dozero, true }, { "zero", dozero, true, false },
{ "lockstats", lockstats, true }, { "lockstats", lockstats, true, true },
{ NULL, NULL, false } { NULL, NULL, false, false }
}; };
static void checkcommand(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, char group) static void checkcommand(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, char group)
@ -3113,6 +3122,49 @@ static void checkcommand(struct io_data *io_data, __maybe_unused SOCKETTYPE c, c
io_close(io_data); io_close(io_data);
} }
static void head_join(struct io_data *io_data, char *cmdptr, bool isjson, bool *firstjoin)
{
char *ptr;
if (*firstjoin) {
if (isjson)
io_add(io_data, JSON0);
*firstjoin = false;
} else {
if (isjson)
io_add(io_data, JSON_BETWEEN_JOIN);
}
// External supplied string
ptr = escape_string(cmdptr, isjson);
if (isjson) {
io_add(io_data, JSON1);
io_add(io_data, ptr);
io_add(io_data, JSON2);
} else {
io_add(io_data, JOIN_CMD);
io_add(io_data, ptr);
io_add(io_data, BETWEEN_JOIN);
}
if (ptr != cmdptr)
free(ptr);
}
static void tail_join(struct io_data *io_data, bool isjson)
{
if (io_data->close) {
io_add(io_data, JSON_CLOSE);
io_data->close = false;
}
if (isjson) {
io_add(io_data, JSON_END);
io_add(io_data, JSON3);
}
}
static void send_result(struct io_data *io_data, SOCKETTYPE c, bool isjson) static void send_result(struct io_data *io_data, SOCKETTYPE c, bool isjson)
{ {
int count, sendc, res, tosend, len, n; int count, sendc, res, tosend, len, n;
@ -3677,7 +3729,7 @@ void api(int api_thr_id)
struct sockaddr_in cli; struct sockaddr_in cli;
socklen_t clisiz; socklen_t clisiz;
char cmdbuf[100]; char cmdbuf[100];
char *cmd = NULL; char *cmd = NULL, *cmdptr, *cmdsbuf;
char *param; char *param;
bool addrok; bool addrok;
char group; char group;
@ -3685,7 +3737,7 @@ void api(int api_thr_id)
json_t *json_config = NULL; json_t *json_config = NULL;
json_t *json_val; json_t *json_val;
bool isjson; bool isjson;
bool did; bool did, isjoin, firstjoin;
int i; int i;
SOCKETTYPE *apisock; SOCKETTYPE *apisock;
@ -3886,9 +3938,46 @@ void api(int api_thr_id)
} }
if (!did) { if (!did) {
if (strchr(cmd, CMDJOIN)) {
firstjoin = isjoin = true;
// cmd + leading '|' + '\0'
cmdsbuf = malloc(strlen(cmd) + 2);
if (!cmdsbuf)
quithere(1, "OOM cmdsbuf");
strcpy(cmdsbuf, "|");
param = NULL;
} else
firstjoin = isjoin = false;
cmdptr = cmd;
do {
did = false;
if (isjoin) {
cmd = strchr(cmdptr, CMDJOIN);
if (cmd)
*(cmd++) = '\0';
if (!*cmdptr)
goto inochi;
}
for (i = 0; cmds[i].name != NULL; i++) { for (i = 0; cmds[i].name != NULL; i++) {
if (strcmp(cmd, cmds[i].name) == 0) { if (strcmp(cmdptr, cmds[i].name) == 0) {
sprintf(cmdbuf, "|%s|", cmd); sprintf(cmdbuf, "|%s|", cmdptr);
if (isjoin) {
if (strstr(cmdsbuf, cmdbuf)) {
did = true;
break;
}
strcat(cmdsbuf, cmdptr);
strcat(cmdsbuf, "|");
head_join(io_data, cmdptr, isjson, &firstjoin);
if (!cmds[i].joinable) {
message(io_data, MSG_ACCDENY, 0, cmds[i].name, isjson);
did = true;
tail_join(io_data, isjson);
break;
}
}
if (ISPRIVGROUP(group) || strstr(COMMANDS(group), cmdbuf)) if (ISPRIVGROUP(group) || strstr(COMMANDS(group), cmdbuf))
(cmds[i].func)(io_data, c, param, isjson, group); (cmds[i].func)(io_data, c, param, isjson, group);
else { else {
@ -3896,17 +3985,33 @@ void api(int api_thr_id)
applog(LOG_DEBUG, "API: access denied to '%s' for '%s' command", connectaddr, cmds[i].name); applog(LOG_DEBUG, "API: access denied to '%s' for '%s' command", connectaddr, cmds[i].name);
} }
send_result(io_data, c, isjson);
did = true; did = true;
if (!isjoin)
send_result(io_data, c, isjson);
else
tail_join(io_data, isjson);
break; break;
} }
} }
}
if (!did) { if (!did) {
if (isjoin)
head_join(io_data, cmdptr, isjson, &firstjoin);
message(io_data, MSG_INVCMD, 0, NULL, isjson); message(io_data, MSG_INVCMD, 0, NULL, isjson);
if (isjoin)
tail_join(io_data, isjson);
else
send_result(io_data, c, isjson); send_result(io_data, c, isjson);
} }
inochi:
if (isjoin)
cmdptr = cmd;
} while (isjoin && cmdptr);
}
if (isjoin)
send_result(io_data, c, isjson);
if (isjson && json_is_object(json_config)) if (isjson && json_is_object(json_config))
json_decref(json_config); json_decref(json_config);
} }

38
doc/API

@ -109,6 +109,36 @@ The STATUS section is:
This defaults to the sgminer version but is the value of --api-description This defaults to the sgminer version but is the value of --api-description
if it was specified at runtime. if it was specified at runtime.
With API V3.1 you can also request multiple report replies in a single command
request
e.g. to request both summary and devs, the command would be summary+devs
This is only available for report commands that don't need parameters,
and is not available for commands that change anything
Any parameters supplied will be ignored
The extra formatting of the result is to have a section for each command
e.g. CMD=summary|STATUS=....|CMD=devs|STATUS=...
With JSON, each result is within a section of the command name
e.g. {"summary":{"STATUS":[{"STATUS":"S"...}],"SUMMARY":[...],"id":1},
"devs":{"STATUS":[{"STATUS:"S"...}],"DEVS":[...],"id":1},"id":1}
As before, if you supply bad JSON you'll just get a single 'E' STATUS section
in the old format, since it doesn't switch to using the new format until it
correctly processes the JSON and can match a '+' in the command
If you request a command multiple times, e.g. devs+devs
you'll just get it once
If this results in only one command, it will still use the new layout
with just the one command
If you request a command that can't be used due to requiring parameters,
a command that isn't a report, or an invalid command, you'll get an 'E' STATUS
for that one but it will still attempt to process all other commands supplied
Blank/missing commands are ignore e.g. +devs++
will just show 'devs' using the new layout
For API version 1.10 and later: For API version 1.10 and later:
The list of requests - a (*) means it requires privileged access - and replies: The list of requests - a (*) means it requires privileged access - and replies:
@ -497,7 +527,13 @@ miner.php - an example web page to access the API
Feature Changelog for external applications using the API: Feature Changelog for external applications using the API:
API V3.0 (cgminer v3.9.1) API V3.1 (cgminer v3.12.1)
Multiple report request command with '+' e.g. summary+devs
---------
API V3.0 (cgminer v3.11.0)
Allow unlimited size replies Allow unlimited size replies

9
miner.h

@ -355,6 +355,9 @@ struct device_drv {
void (*thread_shutdown)(struct thr_info *); void (*thread_shutdown)(struct thr_info *);
void (*thread_enable)(struct thr_info *); void (*thread_enable)(struct thr_info *);
/* What should be zeroed in this device when global zero stats is sent */
void (*zero_stats)(struct cgpu_info *);
// Does it need to be free()d? // Does it need to be free()d?
bool copy; bool copy;
@ -1382,7 +1385,10 @@ extern bool submit_nonce(struct thr_info *thr, struct work *work, uint32_t nonce
extern bool submit_noffset_nonce(struct thr_info *thr, struct work *work, uint32_t nonce, extern bool submit_noffset_nonce(struct thr_info *thr, struct work *work, uint32_t nonce,
int noffset); int noffset);
extern struct work *get_work(struct thr_info *thr, const int thr_id); extern struct work *get_work(struct thr_info *thr, const int thr_id);
extern void __add_queued(struct cgpu_info *cgpu, struct work *work);
extern struct work *get_queued(struct cgpu_info *cgpu); extern struct work *get_queued(struct cgpu_info *cgpu);
extern void add_queued(struct cgpu_info *cgpu, struct work *work);
extern struct work *get_queue_work(struct thr_info *thr, struct cgpu_info *cgpu, int thr_id);
extern struct work *__find_work_bymidstate(struct work *que, char *midstate, size_t midstatelen, char *data, int offset, size_t datalen); extern struct work *__find_work_bymidstate(struct work *que, char *midstate, size_t midstatelen, char *data, int offset, size_t datalen);
extern struct work *find_queued_work_bymidstate(struct cgpu_info *cgpu, char *midstate, size_t midstatelen, char *data, int offset, size_t datalen); extern struct work *find_queued_work_bymidstate(struct cgpu_info *cgpu, char *midstate, size_t midstatelen, char *data, int offset, size_t datalen);
extern struct work *clone_queued_work_bymidstate(struct cgpu_info *cgpu, char *midstate, size_t midstatelen, char *data, int offset, size_t datalen); extern struct work *clone_queued_work_bymidstate(struct cgpu_info *cgpu, char *midstate, size_t midstatelen, char *data, int offset, size_t datalen);
@ -1390,6 +1396,7 @@ extern void __work_completed(struct cgpu_info *cgpu, struct work *work);
extern int age_queued_work(struct cgpu_info *cgpu, double secs); extern int age_queued_work(struct cgpu_info *cgpu, double secs);
extern void work_completed(struct cgpu_info *cgpu, struct work *work); extern void work_completed(struct cgpu_info *cgpu, struct work *work);
extern struct work *take_queued_work_bymidstate(struct cgpu_info *cgpu, char *midstate, size_t midstatelen, char *data, int offset, size_t datalen); extern struct work *take_queued_work_bymidstate(struct cgpu_info *cgpu, char *midstate, size_t midstatelen, char *data, int offset, size_t datalen);
extern void flush_queue(struct cgpu_info *cgpu);
extern void hash_driver_work(struct thr_info *mythr); extern void hash_driver_work(struct thr_info *mythr);
extern void hash_queued_work(struct thr_info *mythr); extern void hash_queued_work(struct thr_info *mythr);
extern void _wlog(const char *str); extern void _wlog(const char *str);
@ -1434,6 +1441,7 @@ enum api_data_type {
API_INT, API_INT,
API_UINT, API_UINT,
API_UINT32, API_UINT32,
API_HEX32,
API_UINT64, API_UINT64,
API_DOUBLE, API_DOUBLE,
API_ELAPSED, API_ELAPSED,
@ -1470,6 +1478,7 @@ extern struct api_data *api_add_uint16(struct api_data *root, char *name, uint16
extern struct api_data *api_add_int(struct api_data *root, char *name, int *data, bool copy_data); extern struct api_data *api_add_int(struct api_data *root, char *name, int *data, bool copy_data);
extern struct api_data *api_add_uint(struct api_data *root, char *name, unsigned int *data, bool copy_data); extern struct api_data *api_add_uint(struct api_data *root, char *name, unsigned int *data, bool copy_data);
extern struct api_data *api_add_uint32(struct api_data *root, char *name, uint32_t *data, bool copy_data); extern struct api_data *api_add_uint32(struct api_data *root, char *name, uint32_t *data, bool copy_data);
extern struct api_data *api_add_hex32(struct api_data *root, char *name, uint32_t *data, bool copy_data);
extern struct api_data *api_add_uint64(struct api_data *root, char *name, uint64_t *data, bool copy_data); extern struct api_data *api_add_uint64(struct api_data *root, char *name, uint64_t *data, bool copy_data);
extern struct api_data *api_add_double(struct api_data *root, char *name, double *data, bool copy_data); extern struct api_data *api_add_double(struct api_data *root, char *name, double *data, bool copy_data);
extern struct api_data *api_add_elapsed(struct api_data *root, char *name, double *data, bool copy_data); extern struct api_data *api_add_elapsed(struct api_data *root, char *name, double *data, bool copy_data);

115
sgminer.c

@ -260,6 +260,7 @@ struct stratum_share {
struct work *work; struct work *work;
int id; int id;
time_t sshare_time; time_t sshare_time;
time_t sshare_sent;
}; };
static struct stratum_share *stratum_shares = NULL; static struct stratum_share *stratum_shares = NULL;
@ -2935,7 +2936,7 @@ static void calc_diff(struct work *work, double known)
{ {
struct sgminer_pool_stats *pool_stats = &(work->pool->sgminer_pool_stats); struct sgminer_pool_stats *pool_stats = &(work->pool->sgminer_pool_stats);
double difficulty; double difficulty;
int intdiff; uint64_t uintdiff;
if (known) if (known)
work->work_difficulty = known; work->work_difficulty = known;
@ -2952,8 +2953,8 @@ static void calc_diff(struct work *work, double known)
difficulty = work->work_difficulty; difficulty = work->work_difficulty;
pool_stats->last_diff = difficulty; pool_stats->last_diff = difficulty;
intdiff = round(difficulty); uintdiff = round(difficulty);
suffix_string(intdiff, work->pool->diff, sizeof(work->pool->diff), 0); suffix_string(uintdiff, work->pool->diff, sizeof(work->pool->diff), 0);
if (difficulty == pool_stats->min_diff) if (difficulty == pool_stats->min_diff)
pool_stats->min_diff_count++; pool_stats->min_diff_count++;
@ -3767,8 +3768,6 @@ int restart_wait(struct thr_info *thr, unsigned int mstime)
return rc; return rc;
} }
static void flush_queue(struct cgpu_info *cgpu);
static void *restart_thread(void __maybe_unused *arg) static void *restart_thread(void __maybe_unused *arg)
{ {
struct pool *cp = current_pool(); struct pool *cp = current_pool();
@ -4436,6 +4435,11 @@ void zero_stats(void)
cgpu->diff_rejected = 0; cgpu->diff_rejected = 0;
cgpu->last_share_diff = 0; cgpu->last_share_diff = 0;
mutex_unlock(&hash_lock); mutex_unlock(&hash_lock);
/* Don't take any locks in the driver zero stats function, as
* it's called async from everything else and we don't want to
* deadlock. */
cgpu->drv->zero_stats(cgpu);
} }
} }
@ -5022,8 +5026,15 @@ static void stratum_share_result(json_t *val, json_t *res_val, json_t *err_val,
struct stratum_share *sshare) struct stratum_share *sshare)
{ {
struct work *work = sshare->work; struct work *work = sshare->work;
time_t now_t = time(NULL);
char hashshow[64]; char hashshow[64];
int srdiff;
srdiff = now_t - sshare->sshare_sent;
if (opt_debug || srdiff > 0) {
applog(LOG_INFO, "Pool %d stratum share result lag time %d seconds",
work->pool->pool_no, srdiff);
}
show_hash(work, hashshow); show_hash(work, hashshow);
share_result(val, res_val, err_val, work, hashshow, false, ""); share_result(val, res_val, err_val, work, hashshow, false, "");
} }
@ -5468,6 +5479,15 @@ static void *stratum_sthread(void *userdata)
free(sshare); free(sshare);
pool->stale_shares++; pool->stale_shares++;
total_stale++; total_stale++;
} else {
int ssdiff;
sshare->sshare_sent = time(NULL);
ssdiff = sshare->sshare_sent - sshare->sshare_time;
if (opt_debug || ssdiff > 0) {
applog(LOG_INFO, "Pool %d stratum share submission lag time %d seconds",
pool->pool_no, ssdiff);
}
} }
} }
@ -5693,13 +5713,18 @@ static void pool_resus(struct pool *pool)
applog(LOG_INFO, "%s alive", pool->poolname); applog(LOG_INFO, "%s alive", pool->poolname);
} }
static struct work *hash_pop(void) /* If this is called non_blocking, it will return NULL for work so that must
* be handled. */
static struct work *hash_pop(bool blocking)
{ {
struct work *work = NULL, *tmp; struct work *work = NULL, *tmp;
int hc; int hc;
mutex_lock(stgd_lock); mutex_lock(stgd_lock);
while (!HASH_COUNT(staged_work)) { if (!HASH_COUNT(staged_work)) {
if (!blocking)
goto out_unlock;
do {
struct timespec then; struct timespec then;
struct timeval now; struct timeval now;
int rc; int rc;
@ -5716,6 +5741,7 @@ static struct work *hash_pop(void)
no_work = true; no_work = true;
applog(LOG_WARNING, "Waiting for work to be available from pools."); applog(LOG_WARNING, "Waiting for work to be available from pools.");
} }
} while (!HASH_COUNT(staged_work));
} }
if (no_work) { if (no_work) {
@ -5741,6 +5767,10 @@ static struct work *hash_pop(void)
/* Signal hash_pop again in case there are mutliple hash_pop waiters */ /* Signal hash_pop again in case there are mutliple hash_pop waiters */
pthread_cond_signal(&getq->cond); pthread_cond_signal(&getq->cond);
/* Keep track of last getwork grabbed */
last_getwork = time(NULL);
out_unlock:
mutex_unlock(stgd_lock); mutex_unlock(stgd_lock);
return work; return work;
@ -5889,18 +5919,27 @@ static void gen_stratum_work(struct pool *pool, struct work *work)
struct work *get_work(struct thr_info *thr, const int thr_id) struct work *get_work(struct thr_info *thr, const int thr_id)
{ {
struct work *work = NULL; struct work *work = NULL;
time_t diff_t;
thread_reportout(thr); thread_reportout(thr);
applog(LOG_DEBUG, "Popping work from get queue to get work"); applog(LOG_DEBUG, "Popping work from get queue to get work");
diff_t = time(NULL);
while (!work) { while (!work) {
work = hash_pop(); work = hash_pop(true);
if (stale_work(work, false)) { if (stale_work(work, false)) {
discard_work(work); discard_work(work);
work = NULL; work = NULL;
wake_gws(); wake_gws();
} }
} }
last_getwork = time(NULL); diff_t = time(NULL) - diff_t;
/* Since this is a blocking function, we need to add grace time to
* the device's last valid work to not make outages appear to be
* device failures. */
if (diff_t > 0) {
applog(LOG_DEBUG, "Get work blocked for %d seconds", (int)diff_t);
thr->cgpu->last_device_valid_work += diff_t;
}
applog(LOG_DEBUG, "Got work from get queue to get work for thread %d", thr_id); applog(LOG_DEBUG, "Got work from get queue to get work for thread %d", thr_id);
work->thr_id = thr_id; work->thr_id = thr_id;
@ -6033,7 +6072,8 @@ bool submit_tested_work(struct thr_info *thr, struct work *work)
update_work_stats(thr, work); update_work_stats(thr, work);
if (!fulltest(work->hash, work->target)) { if (!fulltest(work->hash, work->target)) {
applog(LOG_INFO, "Share above target"); applog(LOG_INFO, "%s %d: Share above target", thr->cgpu->drv->name,
thr->cgpu->device_id);
return false; return false;
} }
work_out = copy_work(work); work_out = copy_work(work);
@ -6072,7 +6112,8 @@ bool submit_noffset_nonce(struct thr_info *thr, struct work *work_in, uint32_t n
ret = true; ret = true;
update_work_stats(thr, work); update_work_stats(thr, work);
if (!fulltest(work->hash, work->target)) { if (!fulltest(work->hash, work->target)) {
applog(LOG_INFO, "Share above target"); applog(LOG_INFO, "%s %d: Share above target", thr->cgpu->drv->name,
thr->cgpu->device_id);
goto out; goto out;
} }
submit_work_async(work); submit_work_async(work);
@ -6305,6 +6346,13 @@ static void fill_queue(struct thr_info *mythr, struct cgpu_info *cgpu, struct de
} while (!drv->queue_full(cgpu)); } while (!drv->queue_full(cgpu));
} }
/* Add a work item to a cgpu's queued hashlist */
void __add_queued(struct cgpu_info *cgpu, struct work *work)
{
cgpu->queued_count++;
HASH_ADD_INT(cgpu->queued_work, id, work);
}
/* This function is for retrieving one work item from the unqueued pointer and /* This function is for retrieving one work item from the unqueued pointer and
* adding it to the hashtable of queued work. Code using this function must be * adding it to the hashtable of queued work. Code using this function must be
* able to handle NULL as a return which implies there is no work available. */ * able to handle NULL as a return which implies there is no work available. */
@ -6315,7 +6363,12 @@ struct work *get_queued(struct cgpu_info *cgpu)
wr_lock(&cgpu->qlock); wr_lock(&cgpu->qlock);
if (cgpu->unqueued_work) { if (cgpu->unqueued_work) {
work = cgpu->unqueued_work; work = cgpu->unqueued_work;
HASH_ADD_INT(cgpu->queued_work, id, work); if (unlikely(stale_work(work, false))) {
discard_work(work);
work = NULL;
wake_gws();
} else
__add_queued(cgpu, work);
cgpu->unqueued_work = NULL; cgpu->unqueued_work = NULL;
} }
wr_unlock(&cgpu->qlock); wr_unlock(&cgpu->qlock);
@ -6323,6 +6376,22 @@ struct work *get_queued(struct cgpu_info *cgpu)
return work; return work;
} }
void add_queued(struct cgpu_info *cgpu, struct work *work)
{
wr_lock(&cgpu->qlock);
__add_queued(cgpu, work);
wr_unlock(&cgpu->qlock);
}
/* Get fresh work and add it to cgpu's queued hashlist */
struct work *get_queue_work(struct thr_info *thr, struct cgpu_info *cgpu, int thr_id)
{
struct work *work = get_work(thr, thr_id);
add_queued(cgpu, work);
return work;
}
/* This function is for finding an already queued work item in the /* This function is for finding an already queued work item in the
* given que hashtable. Code using this function must be able * given que hashtable. Code using this function must be able
* to handle NULL as a return which implies there is no matching work. * to handle NULL as a return which implies there is no matching work.
@ -6426,7 +6495,7 @@ struct work *take_queued_work_bymidstate(struct cgpu_info *cgpu, char *midstate,
return work; return work;
} }
static void flush_queue(struct cgpu_info *cgpu) void flush_queue(struct cgpu_info *cgpu)
{ {
struct work *work = NULL; struct work *work = NULL;
@ -7512,6 +7581,7 @@ static void noop_detect(bool __maybe_unused hotplug)
#define noop_flush_work noop_reinit_device #define noop_flush_work noop_reinit_device
#define noop_update_work noop_reinit_device #define noop_update_work noop_reinit_device
#define noop_queue_full noop_get_stats #define noop_queue_full noop_get_stats
#define noop_zero_stats noop_reinit_device
/* Fill missing driver drv functions with noops */ /* Fill missing driver drv functions with noops */
void fill_device_drv(struct device_drv *drv) void fill_device_drv(struct device_drv *drv)
@ -7548,6 +7618,8 @@ void fill_device_drv(struct device_drv *drv)
drv->update_work = &noop_update_work; drv->update_work = &noop_update_work;
if (!drv->queue_full) if (!drv->queue_full)
drv->queue_full = &noop_queue_full; drv->queue_full = &noop_queue_full;
if (!drv->zero_stats)
drv->zero_stats = &noop_zero_stats;
if (!drv->max_diff) if (!drv->max_diff)
drv->max_diff = 1; drv->max_diff = 1;
if (!drv->working_diff) if (!drv->working_diff)
@ -8078,6 +8150,8 @@ begin_bench:
int ts, max_staged = opt_queue; int ts, max_staged = opt_queue;
struct pool *pool, *cp; struct pool *pool, *cp;
bool lagging = false; bool lagging = false;
struct timespec then;
struct timeval now;
struct work *work; struct work *work;
if (opt_work_update) if (opt_work_update)
@ -8090,6 +8164,10 @@ begin_bench:
if (!pool_localgen(cp) && !staged_rollable) if (!pool_localgen(cp) && !staged_rollable)
max_staged += mining_threads; max_staged += mining_threads;
cgtime(&now);
then.tv_sec = now.tv_sec + 2;
then.tv_nsec = now.tv_usec * 1000;
mutex_lock(stgd_lock); mutex_lock(stgd_lock);
ts = __total_staged(); ts = __total_staged();
@ -8098,13 +8176,20 @@ begin_bench:
/* Wait until hash_pop tells us we need to create more work */ /* Wait until hash_pop tells us we need to create more work */
if (ts > max_staged) { if (ts > max_staged) {
pthread_cond_wait(&gws_cond, stgd_lock); pthread_cond_timedwait(&gws_cond, stgd_lock, &then);
ts = __total_staged(); ts = __total_staged();
} }
mutex_unlock(stgd_lock); mutex_unlock(stgd_lock);
if (ts > max_staged) if (ts > max_staged) {
/* Keeps slowly generating work even if it's not being
* used to keep last_getwork incrementing and to see
* if pools are still alive. */
work = hash_pop(false);
if (work)
discard_work(work);
continue; continue;
}
work = make_work(); work = make_work();

35
util.c

@ -1258,11 +1258,14 @@ static enum send_ret __stratum_send(struct pool *pool, char *s, ssize_t len)
struct timeval timeout = {1, 0}; struct timeval timeout = {1, 0};
ssize_t sent; ssize_t sent;
fd_set wd; fd_set wd;
retry:
FD_ZERO(&wd); FD_ZERO(&wd);
FD_SET(sock, &wd); FD_SET(sock, &wd);
if (select(sock + 1, NULL, &wd, NULL, &timeout) < 1) if (select(sock + 1, NULL, &wd, NULL, &timeout) < 1) {
if (interrupted())
goto retry;
return SEND_SELECTFAIL; return SEND_SELECTFAIL;
}
#ifdef __APPLE__ #ifdef __APPLE__
sent = send(pool->sock, s + ssent, len, SO_NOSIGPIPE); sent = send(pool->sock, s + ssent, len, SO_NOSIGPIPE);
#elif WIN32 #elif WIN32
@ -1654,7 +1657,7 @@ static bool parse_diff(struct pool *pool, json_t *val)
if ((double)idiff == diff) if ((double)idiff == diff)
applog(LOG_NOTICE, "%s difficulty changed to %d", pool->poolname ,idiff); applog(LOG_NOTICE, "%s difficulty changed to %d", pool->poolname ,idiff);
else else
applog(LOG_NOTICE, "%s difficulty changed to %f", pool->poolname, diff); applog(LOG_NOTICE, "%s difficulty changed to %.1f", pool->poolname, diff);
} else } else
applog(LOG_DEBUG, "%s difficulty set to %f", pool->poolname, diff); applog(LOG_DEBUG, "%s difficulty set to %f", pool->poolname, diff);
@ -2184,6 +2187,7 @@ static bool setup_stratum_socket(struct pool *pool)
applog(LOG_DEBUG, "Failed sock connect"); applog(LOG_DEBUG, "Failed sock connect");
continue; continue;
} }
retry:
FD_ZERO(&rw); FD_ZERO(&rw);
FD_SET(sockd, &rw); FD_SET(sockd, &rw);
selret = select(sockd + 1, NULL, &rw, NULL, &tv_timeout); selret = select(sockd + 1, NULL, &rw, NULL, &tv_timeout);
@ -2199,6 +2203,8 @@ static bool setup_stratum_socket(struct pool *pool)
break; break;
} }
} }
if (selret < 0 && interrupted())
goto retry;
CLOSESOCKET(sockd); CLOSESOCKET(sockd);
applog(LOG_DEBUG, "Select timeout/failed connect"); applog(LOG_DEBUG, "Select timeout/failed connect");
continue; continue;
@ -2590,19 +2596,24 @@ void _cgsem_post(cgsem_t *cgsem, const char *file, const char *func, const int l
const char buf = 1; const char buf = 1;
int ret; int ret;
retry:
ret = write(cgsem->pipefd[1], &buf, 1); ret = write(cgsem->pipefd[1], &buf, 1);
if (unlikely(ret == 0)) if (unlikely(ret == 0))
applog(LOG_WARNING, "Failed to write errno=%d" IN_FMT_FFL, errno, file, func, line); applog(LOG_WARNING, "Failed to write errno=%d" IN_FMT_FFL, errno, file, func, line);
else if (unlikely(ret < 0 && interrupted))
goto retry;
} }
void _cgsem_wait(cgsem_t *cgsem, const char *file, const char *func, const int line) void _cgsem_wait(cgsem_t *cgsem, const char *file, const char *func, const int line)
{ {
char buf; char buf;
int ret; int ret;
retry:
ret = read(cgsem->pipefd[0], &buf, 1); ret = read(cgsem->pipefd[0], &buf, 1);
if (unlikely(ret == 0)) if (unlikely(ret == 0))
applog(LOG_WARNING, "Failed to read errno=%d" IN_FMT_FFL, errno, file, func, line); applog(LOG_WARNING, "Failed to read errno=%d" IN_FMT_FFL, errno, file, func, line);
else if (unlikely(ret < 0 && interrupted))
goto retry;
} }
void cgsem_destroy(cgsem_t *cgsem) void cgsem_destroy(cgsem_t *cgsem)
@ -2619,6 +2630,7 @@ int _cgsem_mswait(cgsem_t *cgsem, int ms, const char *file, const char *func, co
fd_set rd; fd_set rd;
char buf; char buf;
retry:
fd = cgsem->pipefd[0]; fd = cgsem->pipefd[0];
FD_ZERO(&rd); FD_ZERO(&rd);
FD_SET(fd, &rd); FD_SET(fd, &rd);
@ -2631,6 +2643,8 @@ int _cgsem_mswait(cgsem_t *cgsem, int ms, const char *file, const char *func, co
} }
if (likely(!ret)) if (likely(!ret))
return ETIMEDOUT; return ETIMEDOUT;
if (interrupted())
goto retry;
quitfrom(1, file, func, line, "Failed to sem_timedwait errno=%d cgsem=0x%p", errno, cgsem); quitfrom(1, file, func, line, "Failed to sem_timedwait errno=%d cgsem=0x%p", errno, cgsem);
/* We don't reach here */ /* We don't reach here */
return 0; return 0;
@ -2652,6 +2666,8 @@ void cgsem_reset(cgsem_t *cgsem)
ret = select(fd + 1, &rd, NULL, NULL, &timeout); ret = select(fd + 1, &rd, NULL, NULL, &timeout);
if (ret > 0) if (ret > 0)
ret = read(fd, &buf, 1); ret = read(fd, &buf, 1);
else if (unlikely(ret < 0 && interrupted()))
ret = 1;
} while (ret > 0); } while (ret > 0);
} }
#else #else
@ -2670,9 +2686,13 @@ void _cgsem_post(cgsem_t *cgsem, const char *file, const char *func, const int l
void _cgsem_wait(cgsem_t *cgsem, const char *file, const char *func, const int line) void _cgsem_wait(cgsem_t *cgsem, const char *file, const char *func, const int line)
{ {
if (unlikely(sem_wait(cgsem))) retry:
if (unlikely(sem_wait(cgsem))) {
if (interrupted())
goto retry;
quitfrom(1, file, func, line, "Failed to sem_wait errno=%d cgsem=0x%p", errno, cgsem); quitfrom(1, file, func, line, "Failed to sem_wait errno=%d cgsem=0x%p", errno, cgsem);
} }
}
int _cgsem_mswait(cgsem_t *cgsem, int ms, const char *file, const char *func, const int line) int _cgsem_mswait(cgsem_t *cgsem, int ms, const char *file, const char *func, const int line)
{ {
@ -2683,12 +2703,15 @@ int _cgsem_mswait(cgsem_t *cgsem, int ms, const char *file, const char *func, co
cgtime(&tv_now); cgtime(&tv_now);
timeval_to_spec(&ts_now, &tv_now); timeval_to_spec(&ts_now, &tv_now);
ms_to_timespec(&abs_timeout, ms); ms_to_timespec(&abs_timeout, ms);
retry:
timeraddspec(&abs_timeout, &ts_now); timeraddspec(&abs_timeout, &ts_now);
ret = sem_timedwait(cgsem, &abs_timeout); ret = sem_timedwait(cgsem, &abs_timeout);
if (ret) { if (ret) {
if (likely(sock_timeout())) if (likely(sock_timeout()))
return ETIMEDOUT; return ETIMEDOUT;
if (interrupted())
goto retry;
quitfrom(1, file, func, line, "Failed to sem_timedwait errno=%d cgsem=0x%p", errno, cgsem); quitfrom(1, file, func, line, "Failed to sem_timedwait errno=%d cgsem=0x%p", errno, cgsem);
} }
return 0; return 0;
@ -2700,6 +2723,8 @@ void cgsem_reset(cgsem_t *cgsem)
do { do {
ret = sem_trywait(cgsem); ret = sem_trywait(cgsem);
if (unlikely(ret < 0 && interrupted()))
ret = 0;
} while (!ret); } while (!ret);
} }

14
util.h

@ -24,6 +24,10 @@
{ {
return (errno == ETIMEDOUT); return (errno == ETIMEDOUT);
} }
static inline bool interrupted(void)
{
return (errno == EINTR);
}
#elif defined WIN32 #elif defined WIN32
#include <ws2tcpip.h> #include <ws2tcpip.h>
#include <winsock2.h> #include <winsock2.h>
@ -37,13 +41,19 @@
extern char *WSAErrorMsg(void); extern char *WSAErrorMsg(void);
#define SOCKERRMSG WSAErrorMsg() #define SOCKERRMSG WSAErrorMsg()
/* Check for windows variants of the errors as well as when ming
* decides to wrap the error into the errno equivalent. */
static inline bool sock_blocks(void) static inline bool sock_blocks(void)
{ {
return (WSAGetLastError() == WSAEWOULDBLOCK); return (WSAGetLastError() == WSAEWOULDBLOCK || errno == EAGAIN);
} }
static inline bool sock_timeout(void) static inline bool sock_timeout(void)
{ {
return (errno == WSAETIMEDOUT); return (WSAGetLastError() == WSAETIMEDOUT || errno == ETIMEDOUT);
}
static inline bool interrupted(void)
{
return (WSAGetLastError() == WSAEINTR || errno == EINTR);
} }
#ifndef SHUT_RDWR #ifndef SHUT_RDWR
#define SHUT_RDWR SD_BOTH #define SHUT_RDWR SD_BOTH

Loading…
Cancel
Save