mirror of
https://github.com/GOSTSec/sgminer
synced 2025-01-10 23:08:07 +00:00
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.
This commit is contained in:
commit
80e6307033
2
NEWS.md
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
|
||||||
|
247
api.c
247
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,27 +3938,80 @@ void api(int api_thr_id)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!did) {
|
if (!did) {
|
||||||
for (i = 0; cmds[i].name != NULL; i++) {
|
if (strchr(cmd, CMDJOIN)) {
|
||||||
if (strcmp(cmd, cmds[i].name) == 0) {
|
firstjoin = isjoin = true;
|
||||||
sprintf(cmdbuf, "|%s|", cmd);
|
// cmd + leading '|' + '\0'
|
||||||
if (ISPRIVGROUP(group) || strstr(COMMANDS(group), cmdbuf))
|
cmdsbuf = malloc(strlen(cmd) + 2);
|
||||||
(cmds[i].func)(io_data, c, param, isjson, group);
|
if (!cmdsbuf)
|
||||||
else {
|
quithere(1, "OOM cmdsbuf");
|
||||||
message(io_data, MSG_ACCDENY, 0, cmds[i].name, isjson);
|
strcpy(cmdsbuf, "|");
|
||||||
applog(LOG_DEBUG, "API: access denied to '%s' for '%s' command", connectaddr, cmds[i].name);
|
param = NULL;
|
||||||
}
|
} else
|
||||||
|
firstjoin = isjoin = false;
|
||||||
|
|
||||||
send_result(io_data, c, isjson);
|
cmdptr = cmd;
|
||||||
did = true;
|
do {
|
||||||
break;
|
did = false;
|
||||||
|
if (isjoin) {
|
||||||
|
cmd = strchr(cmdptr, CMDJOIN);
|
||||||
|
if (cmd)
|
||||||
|
*(cmd++) = '\0';
|
||||||
|
if (!*cmdptr)
|
||||||
|
goto inochi;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
for (i = 0; cmds[i].name != NULL; i++) {
|
||||||
|
if (strcmp(cmdptr, cmds[i].name) == 0) {
|
||||||
|
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))
|
||||||
|
(cmds[i].func)(io_data, c, param, isjson, group);
|
||||||
|
else {
|
||||||
|
message(io_data, MSG_ACCDENY, 0, cmds[i].name, isjson);
|
||||||
|
applog(LOG_DEBUG, "API: access denied to '%s' for '%s' command", connectaddr, cmds[i].name);
|
||||||
|
}
|
||||||
|
|
||||||
|
did = true;
|
||||||
|
if (!isjoin)
|
||||||
|
send_result(io_data, c, isjson);
|
||||||
|
else
|
||||||
|
tail_join(io_data, isjson);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!did) {
|
||||||
|
if (isjoin)
|
||||||
|
head_join(io_data, cmdptr, isjson, &firstjoin);
|
||||||
|
message(io_data, MSG_INVCMD, 0, NULL, isjson);
|
||||||
|
if (isjoin)
|
||||||
|
tail_join(io_data, isjson);
|
||||||
|
else
|
||||||
|
send_result(io_data, c, isjson);
|
||||||
|
}
|
||||||
|
inochi:
|
||||||
|
if (isjoin)
|
||||||
|
cmdptr = cmd;
|
||||||
|
} while (isjoin && cmdptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!did) {
|
if (isjoin)
|
||||||
message(io_data, MSG_INVCMD, 0, NULL, isjson);
|
|
||||||
send_result(io_data, c, isjson);
|
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
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
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);
|
||||||
|
145
sgminer.c
145
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,29 +5713,35 @@ 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)) {
|
||||||
struct timespec then;
|
if (!blocking)
|
||||||
struct timeval now;
|
goto out_unlock;
|
||||||
int rc;
|
do {
|
||||||
|
struct timespec then;
|
||||||
|
struct timeval now;
|
||||||
|
int rc;
|
||||||
|
|
||||||
cgtime(&now);
|
cgtime(&now);
|
||||||
then.tv_sec = now.tv_sec + 10;
|
then.tv_sec = now.tv_sec + 10;
|
||||||
then.tv_nsec = now.tv_usec * 1000;
|
then.tv_nsec = now.tv_usec * 1000;
|
||||||
pthread_cond_signal(&gws_cond);
|
pthread_cond_signal(&gws_cond);
|
||||||
rc = pthread_cond_timedwait(&getq->cond, stgd_lock, &then);
|
rc = pthread_cond_timedwait(&getq->cond, stgd_lock, &then);
|
||||||
/* Check again for !no_work as multiple threads may be
|
/* Check again for !no_work as multiple threads may be
|
||||||
* waiting on this condition and another may set the
|
* waiting on this condition and another may set the
|
||||||
* bool separately. */
|
* bool separately. */
|
||||||
if (rc && !no_work) {
|
if (rc && !no_work) {
|
||||||
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
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,8 +2686,12 @@ 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
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…
Reference in New Issue
Block a user