From 7d77c0161988c3f4b763dccc7ccc9534d49fed1f Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Wed, 15 Aug 2012 21:26:50 +1000 Subject: [PATCH 01/25] Get rid of age_work(). --- cgminer.c | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/cgminer.c b/cgminer.c index c56a7769..21b9e3e0 100644 --- a/cgminer.c +++ b/cgminer.c @@ -4655,24 +4655,6 @@ static void *watchpool_thread(void __maybe_unused *userdata) return NULL; } -/* Work is sorted according to age, so discard the oldest work items, leaving - * only 1/3 more staged work item than mining threads */ -static void age_work(void) -{ - int discarded = 0, maxq = (mining_threads + opt_queue) * 4 / 3; - - while (total_staged() > maxq) { - struct work *work = hash_pop(NULL); - - if (unlikely(!work)) - break; - discard_work(work); - discarded++; - } - if (discarded) - applog(LOG_DEBUG, "Aged %d work items", discarded); -} - /* Makes sure the hashmeter keeps going even if mining threads stall, updates * the screen at regular intervals, and restarts threads if they appear to have * died. */ @@ -4700,8 +4682,6 @@ static void *watchdog_thread(void __maybe_unused *userdata) discard_stale(); - age_work(); - queue_request(NULL, false); hashmeter(-1, &zero_tv, 0); From fd0be1bb517b5570d2ada386ede13d2c6a07e6c2 Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Wed, 15 Aug 2012 22:28:09 +1000 Subject: [PATCH 02/25] Queue requests for getwork regardless and test whether we should send for a getwork from the getwork thread itself. --- cgminer.c | 71 +++++++++++++++---------------------------------------- miner.h | 1 + 2 files changed, 20 insertions(+), 52 deletions(-) diff --git a/cgminer.c b/cgminer.c index 21b9e3e0..389effbc 100644 --- a/cgminer.c +++ b/cgminer.c @@ -70,7 +70,7 @@ struct workio_cmd { enum workio_commands cmd; struct thr_info *thr; struct work *work; - bool lagging; + bool needed; }; struct strategies strategies[] = { @@ -1933,7 +1933,7 @@ static inline struct pool *select_pool(bool lagging) cp = current_pool(); - if (pool_strategy != POOL_LOADBALANCE && !lagging) + if (pool_strategy != POOL_LOADBALANCE && (!lagging || opt_fail_only)) pool = cp; else pool = NULL; @@ -1997,6 +1997,8 @@ retry: if (!rc && retries < 3) goto retry; + pool->currently_rolling = !!work->rolltime; + gettimeofday(&tv_end, NULL); timersub(&tv_end, &tv_start, &tv_elapsed); pool_stats->getwork_wait_rolling += ((double)tv_elapsed.tv_sec + ((double)tv_elapsed.tv_usec / 1000000)) * 0.63; @@ -2285,10 +2287,19 @@ static int global_queued(void) return ret; } -static bool enough_work(void) +static void *get_work_thread(void *userdata) { + struct workio_cmd *wc = (struct workio_cmd *)userdata; int cq, cs, ts, tq, maxq = opt_queue + mining_threads; struct pool *pool = current_pool(); + struct curl_ent *ce = NULL; + struct work *ret_work; + bool lagging = false; + int failures = 0; + + pthread_detach(pthread_self()); + + applog(LOG_DEBUG, "Creating extra get work thread"); mutex_lock(&qd_lock); cq = __pool_queued(pool); @@ -2300,27 +2311,9 @@ static bool enough_work(void) ts = __total_staged(); mutex_unlock(stgd_lock); - if (((cs || cq >= opt_queue) && ts >= maxq) || - ((cs || cq) && tq >= maxq)) - return true; - return false; -} - -/* ce and pool may appear uninitialised at push_curl_entry, but they're always - * set when we don't have opt_benchmark enabled */ -static void *get_work_thread(void *userdata) -{ - struct workio_cmd *wc = (struct workio_cmd *)userdata; - struct pool * uninitialised_var(pool); - struct curl_ent *ce = NULL; - struct work *ret_work; - int failures = 0; - - pthread_detach(pthread_self()); - - applog(LOG_DEBUG, "Creating extra get work thread"); - - if (!wc->lagging && enough_work()) + if (!ts) + lagging = true; + else if (((cs || cq >= opt_queue) && ts >= maxq) || ((cs || cq) && tq >= maxq)) goto out; ret_work = make_work(); @@ -2332,7 +2325,7 @@ static void *get_work_thread(void *userdata) if (opt_benchmark) get_benchmark_work(ret_work); else { - pool = ret_work->pool = select_pool(wc->lagging); + pool = ret_work->pool = select_pool(lagging); inc_queued(pool); ce = pop_curl_entry(pool); @@ -3820,33 +3813,7 @@ static void pool_resus(struct pool *pool) bool queue_request(struct thr_info *thr, bool needed) { - int cq, cs, ts, tq, maxq = opt_queue + mining_threads; - struct pool *pool = current_pool(); struct workio_cmd *wc; - bool lag = false; - - mutex_lock(&qd_lock); - cq = __pool_queued(pool); - tq = __global_queued(); - mutex_unlock(&qd_lock); - - mutex_lock(stgd_lock); - cs = __pool_staged(pool); - ts = __total_staged(); - mutex_unlock(stgd_lock); - - if (needed && cq >= maxq && !ts && !opt_fail_only) { - /* If we're queueing work faster than we can stage it, consider - * the system lagging and allow work to be gathered from - * another pool if possible */ - lag = true; - } else { - /* Test to make sure we have enough work for pools without rolltime - * and enough original work for pools with rolltime */ - if (((cs || cq >= opt_queue) && ts >= maxq) || - ((cs || cq) && tq >= maxq)) - return true; - } /* fill out work request message */ wc = calloc(1, sizeof(*wc)); @@ -3857,7 +3824,7 @@ bool queue_request(struct thr_info *thr, bool needed) wc->cmd = WC_GET_WORK; wc->thr = thr; - wc->lagging = lag; + wc->needed = needed; applog(LOG_DEBUG, "Queueing getwork request to work thread"); diff --git a/miner.h b/miner.h index f87612ab..f3e51eda 100644 --- a/miner.h +++ b/miner.h @@ -735,6 +735,7 @@ struct pool { bool submit_old; bool removed; bool lp_started; + bool currently_rolling; char *hdr_path; char *lp_url; From 611f1cec7c17f65071f85ff621ea6e9dcfbf7e3a Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Wed, 15 Aug 2012 22:48:08 +1000 Subject: [PATCH 03/25] Roll work whenever we can on getwork. --- cgminer.c | 136 +++++++++++++++++++++++++++++++++--------------------- miner.h | 1 - 2 files changed, 83 insertions(+), 54 deletions(-) diff --git a/cgminer.c b/cgminer.c index 389effbc..2a447089 100644 --- a/cgminer.c +++ b/cgminer.c @@ -1997,8 +1997,6 @@ retry: if (!rc && retries < 3) goto retry; - pool->currently_rolling = !!work->rolltime; - gettimeofday(&tv_end, NULL); timersub(&tv_end, &tv_start, &tv_elapsed); pool_stats->getwork_wait_rolling += ((double)tv_elapsed.tv_sec + ((double)tv_elapsed.tv_usec / 1000000)) * 0.63; @@ -2287,6 +2285,86 @@ static int global_queued(void) return ret; } +static bool stale_work(struct work *work, bool share); + +static inline bool should_roll(struct work *work) +{ + if (work->pool == current_pool() || pool_strategy == POOL_LOADBALANCE) + return true; + return false; +} + +/* Limit rolls to 7000 to not beyond 2 hours in the future where bitcoind will + * reject blocks as invalid. */ +static inline bool can_roll(struct work *work) +{ + return (work->pool && work->rolltime && !work->clone && + work->rolls < 7000 && !stale_work(work, false)); +} + +static void roll_work(struct work *work) +{ + uint32_t *work_ntime; + uint32_t ntime; + + work_ntime = (uint32_t *)(work->data + 68); + ntime = be32toh(*work_ntime); + ntime++; + *work_ntime = htobe32(ntime); + local_work++; + work->rolls++; + work->blk.nonce = 0; + applog(LOG_DEBUG, "Successfully rolled work"); + + /* This is now a different work item so it needs a different ID for the + * hashtable */ + work->id = total_work++; +} + +static struct work *make_clone(struct work *work) +{ + struct work *work_clone = make_work(); + + memcpy(work_clone, work, sizeof(struct work)); + work_clone->clone = true; + work_clone->longpoll = false; + work_clone->mandatory = false; + /* Make cloned work appear slightly older to bias towards keeping the + * master work item which can be further rolled */ + work_clone->tv_staged.tv_sec -= 1; + + return work_clone; +} + +static bool stage_work(struct work *work); + +static bool clone_available(void) +{ + struct work *work, *tmp; + bool cloned = false; + + mutex_lock(stgd_lock); + HASH_ITER(hh, staged_work, work, tmp) { + if (can_roll(work) && should_roll(work)) { + struct work *work_clone; + + roll_work(work); + work_clone = make_clone(work); + roll_work(work); + applog(LOG_DEBUG, "Pushing cloned available work to stage thread"); + if (unlikely(!stage_work(work_clone))) { + free(work_clone); + break; + } + cloned = true; + break; + } + } + mutex_unlock(stgd_lock); + + return cloned; +} + static void *get_work_thread(void *userdata) { struct workio_cmd *wc = (struct workio_cmd *)userdata; @@ -2313,8 +2391,9 @@ static void *get_work_thread(void *userdata) if (!ts) lagging = true; - else if (((cs || cq >= opt_queue) && ts >= maxq) || ((cs || cq) && tq >= maxq)) - goto out; + else if (((cs || cq >= opt_queue) && ts >= maxq) || + ((cs || cq) && tq >= maxq) || clone_available()) + goto out; ret_work = make_work(); if (wc->thr) @@ -3859,40 +3938,6 @@ static struct work *hash_pop(const struct timespec *abstime) return work; } -static inline bool should_roll(struct work *work) -{ - if (work->pool == current_pool() || pool_strategy == POOL_LOADBALANCE) - return true; - return false; -} - -/* Limit rolls to 7000 to not beyond 2 hours in the future where bitcoind will - * reject blocks as invalid. */ -static inline bool can_roll(struct work *work) -{ - return (work->pool && work->rolltime && !work->clone && - work->rolls < 7000 && !stale_work(work, false)); -} - -static void roll_work(struct work *work) -{ - uint32_t *work_ntime; - uint32_t ntime; - - work_ntime = (uint32_t *)(work->data + 68); - ntime = be32toh(*work_ntime); - ntime++; - *work_ntime = htobe32(ntime); - local_work++; - work->rolls++; - work->blk.nonce = 0; - applog(LOG_DEBUG, "Successfully rolled work"); - - /* This is now a different work item so it needs a different ID for the - * hashtable */ - work->id = total_work++; -} - static bool reuse_work(struct work *work) { if (can_roll(work) && should_roll(work)) { @@ -3902,21 +3947,6 @@ static bool reuse_work(struct work *work) return false; } -static struct work *make_clone(struct work *work) -{ - struct work *work_clone = make_work(); - - memcpy(work_clone, work, sizeof(struct work)); - work_clone->clone = true; - work_clone->longpoll = false; - work_clone->mandatory = false; - /* Make cloned work appear slightly older to bias towards keeping the - * master work item which can be further rolled */ - work_clone->tv_staged.tv_sec -= 1; - - return work_clone; -} - /* Clones work by rolling it if possible, and returning a clone instead of the * original work item which gets staged again to possibly be rolled again in * the future */ diff --git a/miner.h b/miner.h index f3e51eda..f87612ab 100644 --- a/miner.h +++ b/miner.h @@ -735,7 +735,6 @@ struct pool { bool submit_old; bool removed; bool lp_started; - bool currently_rolling; char *hdr_path; char *lp_url; From 44e81218fd19f0158e1fd2a02fc312c67bbb4cc0 Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Wed, 15 Aug 2012 22:56:18 +1000 Subject: [PATCH 04/25] Factor in opt_queue value into enough work queued or staged. --- cgminer.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cgminer.c b/cgminer.c index 2a447089..a71f3dd3 100644 --- a/cgminer.c +++ b/cgminer.c @@ -2391,9 +2391,10 @@ static void *get_work_thread(void *userdata) if (!ts) lagging = true; - else if (((cs || cq >= opt_queue) && ts >= maxq) || - ((cs || cq) && tq >= maxq) || clone_available()) - goto out; + if (((cs >= opt_queue || cq >= opt_queue) && ts >= maxq) || + ((cs >= opt_queue || cq >= opt_queue) && tq >= maxq) || + clone_available()) + goto out; ret_work = make_work(); if (wc->thr) From a6b97327e11ac23df2d02afc2721a351c09e7937 Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Thu, 16 Aug 2012 00:09:50 +1000 Subject: [PATCH 05/25] Artificially set the pool lagging flag on work restart to avoid messages about slow pools after every longpoll. --- cgminer.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cgminer.c b/cgminer.c index a71f3dd3..85a916d3 100644 --- a/cgminer.c +++ b/cgminer.c @@ -2753,8 +2753,13 @@ int restart_wait(unsigned int mstime) static void restart_threads(void) { + struct pool *cp = current_pool(); int i; + /* Artificially set the lagging flag to avoid pool not providing work + * fast enough messages after every long poll */ + pool_tset(cp, &cp->lagging); + /* Discard staged work that is now stale */ discard_stale(); From 9f1d9ce3b7be85ba4dcca2f5cd2f650e26e28321 Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Thu, 16 Aug 2012 00:16:13 +1000 Subject: [PATCH 06/25] Artificially set the pool lagging flag on pool switch in failover only mode as well. --- cgminer.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cgminer.c b/cgminer.c index 85a916d3..827e217e 100644 --- a/cgminer.c +++ b/cgminer.c @@ -2679,6 +2679,12 @@ void switch_pools(struct pool *selected) pool = currentpool; mutex_unlock(&control_lock); + /* Set the lagging flag to avoid pool not providing work fast enough + * messages in failover only mode since we have to get all fresh work + * as in restart_threads */ + if (opt_fail_only) + pool_tset(pool, &pool->lagging); + if (pool != last_pool) applog(LOG_WARNING, "Switching to %s", pool->rpc_url); From 5fadfdb2195b941ae04e4e12c7c7e2addde9594d Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Thu, 16 Aug 2012 09:19:45 +1000 Subject: [PATCH 07/25] Overlap queued decrementing with staged incrementing. --- cgminer.c | 13 +++++++++++-- miner.h | 1 + 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/cgminer.c b/cgminer.c index 827e217e..6f500f0f 100644 --- a/cgminer.c +++ b/cgminer.c @@ -2427,7 +2427,7 @@ static void *get_work_thread(void *userdata) } fail_pause = opt_fail_pause; - dec_queued(pool); + ret_work->queued = true; } applog(LOG_DEBUG, "Pushing work to requesting thread"); @@ -2890,7 +2890,12 @@ static int tv_sort(struct work *worka, struct work *workb) static bool hash_push(struct work *work) { - bool rc = true; + bool rc = true, dec = false; + + if (work->queued) { + work->queued = false; + dec = true; + } mutex_lock(stgd_lock); if (likely(!getq->frozen)) { @@ -2901,6 +2906,10 @@ static bool hash_push(struct work *work) rc = false; pthread_cond_signal(&getq->cond); mutex_unlock(stgd_lock); + + if (dec) + dec_queued(work->pool); + return rc; } diff --git a/miner.h b/miner.h index f87612ab..d79578ea 100644 --- a/miner.h +++ b/miner.h @@ -795,6 +795,7 @@ struct work { bool stale; bool mandatory; bool block; + bool queued; unsigned int work_block; int id; From afcfea15a7c04875e5dbc37b1cda2ac40926d9b1 Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Thu, 16 Aug 2012 09:30:49 +1000 Subject: [PATCH 08/25] Simplify all those total_secs usages by initialising it to 1 second. --- cgminer.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/cgminer.c b/cgminer.c index 6f500f0f..7b1bbaee 100644 --- a/cgminer.c +++ b/cgminer.c @@ -1367,7 +1367,7 @@ static int pool_staged(struct pool *pool) #ifdef HAVE_CURSES WINDOW *mainwin, *statuswin, *logwin; #endif -double total_secs = 0.1; +double total_secs = 1.0; static char statusline[256]; /* logstart is where the log window should start */ static int devcursor, logstart, logcursor; @@ -1508,7 +1508,7 @@ static void curses_print_devstatus(int thr_id) if (devcursor + cgpu->cgminer_id > LINES - 2) return; - cgpu->utility = cgpu->accepted / ( total_secs ? total_secs : 1 ) * 60; + cgpu->utility = cgpu->accepted / total_secs * 60; wmove(statuswin,devcursor + cgpu->cgminer_id, 0); wprintw(statuswin, " %s %*d: ", cgpu->api->name, dev_width, cgpu->device_id); @@ -1889,7 +1889,7 @@ static bool submit_upstream_work(const struct work *work, CURL *curl) * be stale due to networking delays. */ if (pool->seq_rejects > 10 && !work->stale && opt_disable_pool && enabled_pools > 1) { - double utility = total_accepted / ( total_secs ? total_secs : 1 ) * 60; + double utility = total_accepted / total_secs * 60; if (pool->seq_rejects > utility * 3) { applog(LOG_WARNING, "Pool %d rejected %d sequential shares, disabling!", @@ -1902,7 +1902,7 @@ static bool submit_upstream_work(const struct work *work, CURL *curl) } } - cgpu->utility = cgpu->accepted / ( total_secs ? total_secs : 1 ) * 60; + cgpu->utility = cgpu->accepted / total_secs * 60; if (!opt_realquiet) print_status(thr_id); @@ -3768,7 +3768,7 @@ static void hashmeter(int thr_id, struct timeval *diff, total_secs = (double)total_diff.tv_sec + ((double)total_diff.tv_usec / 1000000.0); - utility = total_accepted / ( total_secs ? total_secs : 1 ) * 60; + utility = total_accepted / total_secs * 60; efficiency = total_getworks ? total_accepted * 100.0 / total_getworks : 0.0; displayed_hashes = total_mhashes_done / total_secs; @@ -4862,7 +4862,7 @@ static void print_summary(void) mins = (diff.tv_sec % 3600) / 60; secs = diff.tv_sec % 60; - utility = total_accepted / ( total_secs ? total_secs : 1 ) * 60; + utility = total_accepted / total_secs * 60; efficiency = total_getworks ? total_accepted * 100.0 / total_getworks : 0.0; applog(LOG_WARNING, "\nSummary of runtime statistics:\n"); @@ -4880,8 +4880,7 @@ static void print_summary(void) mhash_base = false; } - if (total_secs) - applog(LOG_WARNING, "Average hashrate: %.1f %shash/s", displayed_hashes, mhash_base? "Mega" : "Kilo"); + applog(LOG_WARNING, "Average hashrate: %.1f %shash/s", displayed_hashes, mhash_base? "Mega" : "Kilo"); applog(LOG_WARNING, "Solved blocks: %d", found_blocks); applog(LOG_WARNING, "Queued work requests: %d", total_getworks); applog(LOG_WARNING, "Share submissions: %d", total_accepted + total_rejected); From 381c56f811782bbd9b700ff94318a425621fd422 Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Thu, 16 Aug 2012 09:53:36 +1000 Subject: [PATCH 09/25] Check that we'll get 1 shares' worth of work time by rolling before saying we should roll the work. --- cgminer.c | 46 ++++++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/cgminer.c b/cgminer.c index 7b1bbaee..059fc813 100644 --- a/cgminer.c +++ b/cgminer.c @@ -2289,9 +2289,27 @@ static bool stale_work(struct work *work, bool share); static inline bool should_roll(struct work *work) { - if (work->pool == current_pool() || pool_strategy == POOL_LOADBALANCE) - return true; - return false; + struct timeval now; + double share_time; + time_t expiry; + + if (work->pool != current_pool() && pool_strategy != POOL_LOADBALANCE) + return false; + + share_time = total_secs * mining_threads / (total_accepted + 1); + if (work->rolltime > opt_scantime) + expiry = work->rolltime; + else + expiry = opt_scantime; + expiry -= share_time; + + /* We shouldn't roll if we're unlikely to get one shares' duration + * work out of doing so */ + gettimeofday(&now, NULL); + if (now.tv_sec - work->tv_staged.tv_sec > expiry) + return false; + + return true; } /* Limit rolls to 7000 to not beyond 2 hours in the future where bitcoind will @@ -2467,21 +2485,13 @@ static bool stale_work(struct work *work, bool share) struct pool *pool; int getwork_delay; - if (share) { - /* Technically the rolltime should be correct but some pools - * advertise a broken expire= that is lower than a meaningful - * scantime */ - if (work->rolltime > opt_scantime) - work_expiry = work->rolltime; - else - work_expiry = opt_expiry; - } else { - /* Don't keep rolling work right up to the expiration */ - if (work->rolltime > opt_scantime) - work_expiry = (work->rolltime - opt_scantime) * 2 / 3 + opt_scantime; - else /* Shouldn't happen unless someone increases scantime */ - work_expiry = opt_scantime; - } + /* Technically the rolltime should be correct but some pools + * advertise a broken expire= that is lower than a meaningful + * scantime */ + if (work->rolltime > opt_scantime) + work_expiry = work->rolltime; + else + work_expiry = opt_expiry; pool = work->pool; /* Factor in the average getwork delay of this pool, rounding it up to From 8697d6a2cedaf8a395e10f6ae392608ee9b400be Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Thu, 16 Aug 2012 10:16:58 +1000 Subject: [PATCH 10/25] Do the cheaper comparison first. --- cgminer.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cgminer.c b/cgminer.c index 059fc813..bb46f530 100644 --- a/cgminer.c +++ b/cgminer.c @@ -2485,6 +2485,11 @@ static bool stale_work(struct work *work, bool share) struct pool *pool; int getwork_delay; + if (work->work_block != work_block) { + applog(LOG_DEBUG, "Work stale due to block mismatch"); + return true; + } + /* Technically the rolltime should be correct but some pools * advertise a broken expire= that is lower than a meaningful * scantime */ @@ -2507,11 +2512,6 @@ static bool stale_work(struct work *work, bool share) return true; } - if (work->work_block != work_block) { - applog(LOG_DEBUG, "Work stale due to block mismatch"); - return true; - } - if (opt_fail_only && !share && pool != current_pool() && !work->mandatory) { applog(LOG_DEBUG, "Work stale due to fail only pool mismatch"); return true; From 83b9ddfea4ba7d31b19db6de627650064b3325a1 Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Thu, 16 Aug 2012 10:18:30 +1000 Subject: [PATCH 11/25] Make expiry on should_roll to 2/3 time instead of share duration since some hardware will have very fast share times. --- cgminer.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cgminer.c b/cgminer.c index bb46f530..24dc1449 100644 --- a/cgminer.c +++ b/cgminer.c @@ -2290,18 +2290,16 @@ static bool stale_work(struct work *work, bool share); static inline bool should_roll(struct work *work) { struct timeval now; - double share_time; time_t expiry; if (work->pool != current_pool() && pool_strategy != POOL_LOADBALANCE) return false; - share_time = total_secs * mining_threads / (total_accepted + 1); if (work->rolltime > opt_scantime) expiry = work->rolltime; else expiry = opt_scantime; - expiry -= share_time; + expiry = expiry * 2 / 3; /* We shouldn't roll if we're unlikely to get one shares' duration * work out of doing so */ From 0fbd60ae37bdaf98827087b32d9d64c73ec8e641 Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Thu, 16 Aug 2012 10:24:52 +1000 Subject: [PATCH 12/25] Keep track of staged rollable work item counts to speed up clone_available. --- cgminer.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/cgminer.c b/cgminer.c index 24dc1449..9ae23898 100644 --- a/cgminer.c +++ b/cgminer.c @@ -188,7 +188,7 @@ pthread_mutex_t control_lock; int hw_errors; int total_accepted, total_rejected; int total_getworks, total_stale, total_discarded; -static int total_queued; +static int total_queued, staged_rollable; unsigned int new_blocks; static unsigned int work_block; unsigned int found_blocks; @@ -2359,6 +2359,9 @@ static bool clone_available(void) struct work *work, *tmp; bool cloned = false; + if (!staged_rollable) + goto out; + mutex_lock(stgd_lock); HASH_ITER(hh, staged_work, work, tmp) { if (can_roll(work) && should_roll(work)) { @@ -2378,6 +2381,7 @@ static bool clone_available(void) } mutex_unlock(stgd_lock); +out: return cloned; } @@ -2896,6 +2900,11 @@ static int tv_sort(struct work *worka, struct work *workb) return worka->tv_staged.tv_sec - workb->tv_staged.tv_sec; } +static bool work_rollable(struct work *work) +{ + return (!work->clone && work->rolltime); +} + static bool hash_push(struct work *work) { bool rc = true, dec = false; @@ -2906,6 +2915,8 @@ static bool hash_push(struct work *work) } mutex_lock(stgd_lock); + if (work_rollable(work)) + staged_rollable++; if (likely(!getq->frozen)) { HASH_ADD_INT(staged_work, id, work); work->pool->staged++; @@ -3959,6 +3970,8 @@ static struct work *hash_pop(const struct timespec *abstime) work = staged_work; HASH_DEL(staged_work, work); work->pool->staged--; + if (work_rollable(work)) + staged_rollable--; } mutex_unlock(stgd_lock); From c3e32274ee44a167f3b1ccbd6bece46ba9895015 Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Thu, 16 Aug 2012 10:41:01 +1000 Subject: [PATCH 13/25] Cull all the early queue requests since we request every time work is popped now. --- cgminer.c | 26 +++----------------------- driver-bitforce.c | 26 +++----------------------- miner.h | 1 - 3 files changed, 6 insertions(+), 47 deletions(-) diff --git a/cgminer.c b/cgminer.c index 9ae23898..affa56f6 100644 --- a/cgminer.c +++ b/cgminer.c @@ -2630,6 +2630,8 @@ static struct pool *priority_pool(int choice) return ret; } +static bool queue_request(struct thr_info *thr, bool needed); + void switch_pools(struct pool *selected) { struct pool *pool, *last_pool; @@ -2719,8 +2721,6 @@ static void discard_work(struct work *work) free_work(work); } -bool queue_request(struct thr_info *thr, bool needed); - static void discard_stale(void) { struct work *work, *tmp; @@ -2781,8 +2781,6 @@ static void restart_threads(void) /* Discard staged work that is now stale */ discard_stale(); - queue_request(NULL, true); - for (i = 0; i < mining_threads; i++) thr_info[i].work_restart = true; @@ -3930,7 +3928,7 @@ static void pool_resus(struct pool *pool) switch_pools(NULL); } -bool queue_request(struct thr_info *thr, bool needed) +static bool queue_request(struct thr_info *thr, bool needed) { struct workio_cmd *wc; @@ -4370,22 +4368,6 @@ void *miner_thread(void *userdata) } timersub(&tv_end, &tv_workstart, &wdiff); - if (!requested) { - if (wdiff.tv_sec > request_interval || work->blk.nonce > request_nonce) { - thread_reportout(mythr); - if (unlikely(!queue_request(mythr, false))) { - applog(LOG_ERR, "Failed to queue_request in miner_thread %d", thr_id); - - cgpu->device_last_not_well = time(NULL); - cgpu->device_not_well_reason = REASON_THREAD_FAIL_QUEUE; - cgpu->thread_fail_queue_count++; - - goto out; - } - thread_reportin(mythr); - requested = true; - } - } if (unlikely((long)sdiff.tv_sec < cycle)) { int mult; @@ -4721,8 +4703,6 @@ static void *watchdog_thread(void __maybe_unused *userdata) discard_stale(); - queue_request(NULL, false); - hashmeter(-1, &zero_tv, 0); #ifdef HAVE_CURSES diff --git a/driver-bitforce.c b/driver-bitforce.c index ca6b8b03..d80f6648 100644 --- a/driver-bitforce.c +++ b/driver-bitforce.c @@ -602,35 +602,15 @@ static void biforce_thread_enable(struct thr_info *thr) static int64_t bitforce_scanhash(struct thr_info *thr, struct work *work, int64_t __maybe_unused max_nonce) { struct cgpu_info *bitforce = thr->cgpu; - unsigned int sleep_time; bool send_ret; int64_t ret; send_ret = bitforce_send_work(thr, work); - if (!bitforce->nonce_range) { - /* Initially wait 2/3 of the average cycle time so we can request more - work before full scan is up */ - sleep_time = (2 * bitforce->sleep_ms) / 3; - if (!restart_wait(sleep_time)) - return 0; - - bitforce->wait_ms = sleep_time; - queue_request(thr, false); - - /* Now wait athe final 1/3rd; no bitforce should be finished by now */ - sleep_time = bitforce->sleep_ms - sleep_time; - if (!restart_wait(sleep_time)) - return 0; - - bitforce->wait_ms += sleep_time; - } else { - sleep_time = bitforce->sleep_ms; - if (!restart_wait(sleep_time)) - return 0; + if (!restart_wait(bitforce->sleep_ms)) + return 0; - bitforce->wait_ms = sleep_time; - } + bitforce->wait_ms = bitforce->sleep_ms; if (send_ret) { bitforce->polling = true; diff --git a/miner.h b/miner.h index d79578ea..51da4a24 100644 --- a/miner.h +++ b/miner.h @@ -599,7 +599,6 @@ extern pthread_mutex_t restart_lock; extern pthread_cond_t restart_cond; extern void thread_reportin(struct thr_info *thr); -extern bool queue_request(struct thr_info *thr, bool needed); extern int restart_wait(unsigned int mstime); extern void kill_work(void); From 55f7c1498247c4d3facae087933bf6dde7484032 Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Thu, 16 Aug 2012 10:55:57 +1000 Subject: [PATCH 14/25] Grab clones from hashlist wherever possible first. --- cgminer.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/cgminer.c b/cgminer.c index affa56f6..cec02ee7 100644 --- a/cgminer.c +++ b/cgminer.c @@ -3957,15 +3957,24 @@ static bool queue_request(struct thr_info *thr, bool needed) static struct work *hash_pop(const struct timespec *abstime) { - struct work *work = NULL; - int rc = 0; + struct work *work = NULL, *tmp; + int rc = 0, hc; mutex_lock(stgd_lock); while (!getq->frozen && !HASH_COUNT(staged_work) && !rc) rc = pthread_cond_timedwait(&getq->cond, stgd_lock, abstime); - if (HASH_COUNT(staged_work)) { - work = staged_work; + hc = HASH_COUNT(staged_work); + + if (likely(hc)) { + /* Find clone work if possible, to allow masters to be reused */ + if (hc > staged_rollable) { + HASH_ITER(hh, staged_work, work, tmp) { + if (!work_rollable(work)) + break; + } + } else + work = staged_work; HASH_DEL(staged_work, work); work->pool->staged--; if (work_rollable(work)) @@ -4110,7 +4119,6 @@ retry: pool_resus(pool); } - work_heap = clone_work(work_heap); memcpy(work, work_heap, sizeof(struct work)); free_work(work_heap); From ee9e621e1c6c84a909a8a8a042334fbeb28d32d0 Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Thu, 16 Aug 2012 10:59:49 +1000 Subject: [PATCH 15/25] Reinstate clone on grabbing work. --- cgminer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/cgminer.c b/cgminer.c index cec02ee7..27e960d3 100644 --- a/cgminer.c +++ b/cgminer.c @@ -4119,6 +4119,7 @@ retry: pool_resus(pool); } + work_heap = clone_work(work_heap); memcpy(work, work_heap, sizeof(struct work)); free_work(work_heap); From a5f7b953fecb17bc6e286e68c4c31bc7d84afcd8 Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Thu, 16 Aug 2012 11:47:00 +1000 Subject: [PATCH 16/25] Set lagging flag if we're on the last of our staged items. --- cgminer.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cgminer.c b/cgminer.c index 27e960d3..847b084c 100644 --- a/cgminer.c +++ b/cgminer.c @@ -2392,7 +2392,6 @@ static void *get_work_thread(void *userdata) struct pool *pool = current_pool(); struct curl_ent *ce = NULL; struct work *ret_work; - bool lagging = false; int failures = 0; pthread_detach(pthread_self()); @@ -2409,8 +2408,6 @@ static void *get_work_thread(void *userdata) ts = __total_staged(); mutex_unlock(stgd_lock); - if (!ts) - lagging = true; if (((cs >= opt_queue || cq >= opt_queue) && ts >= maxq) || ((cs >= opt_queue || cq >= opt_queue) && tq >= maxq) || clone_available()) @@ -2425,6 +2422,10 @@ static void *get_work_thread(void *userdata) if (opt_benchmark) get_benchmark_work(ret_work); else { + bool lagging; + + if (ts <= opt_queue) + lagging = true; pool = ret_work->pool = select_pool(lagging); inc_queued(pool); From 6d1949e69452f5b3d9f1dc099ce38bd8a743da46 Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Thu, 16 Aug 2012 15:46:41 +1000 Subject: [PATCH 17/25] Make sure we don't opt out of queueing more work if all the queued work is from one pool. --- cgminer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cgminer.c b/cgminer.c index 847b084c..a39a83bc 100644 --- a/cgminer.c +++ b/cgminer.c @@ -2409,7 +2409,7 @@ static void *get_work_thread(void *userdata) mutex_unlock(stgd_lock); if (((cs >= opt_queue || cq >= opt_queue) && ts >= maxq) || - ((cs >= opt_queue || cq >= opt_queue) && tq >= maxq) || + ((cs >= opt_queue || cq >= opt_queue) && tq >= maxq && cq < maxq) || clone_available()) goto out; From e16b7566bdd16ad862b5b0e75476189d8f0a2b2f Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Thu, 16 Aug 2012 15:49:11 +1000 Subject: [PATCH 18/25] Don't keep queueing work indefinitely if we're in opt failover mode. --- cgminer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cgminer.c b/cgminer.c index a39a83bc..1449617d 100644 --- a/cgminer.c +++ b/cgminer.c @@ -2409,7 +2409,7 @@ static void *get_work_thread(void *userdata) mutex_unlock(stgd_lock); if (((cs >= opt_queue || cq >= opt_queue) && ts >= maxq) || - ((cs >= opt_queue || cq >= opt_queue) && tq >= maxq && cq < maxq) || + ((cs >= opt_queue || cq >= opt_queue) && tq >= maxq && ((cq < maxq && !opt_fail_only) || opt_fail_only)) || clone_available()) goto out; From 0e56dffd9233ced418ad0353a41b42b96e309fb7 Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Thu, 16 Aug 2012 17:01:25 +1000 Subject: [PATCH 19/25] Only queue from backup pools once we have nothing staged. --- cgminer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cgminer.c b/cgminer.c index 1449617d..e6a694bb 100644 --- a/cgminer.c +++ b/cgminer.c @@ -2424,7 +2424,7 @@ static void *get_work_thread(void *userdata) else { bool lagging; - if (ts <= opt_queue) + if (!ts) lagging = true; pool = ret_work->pool = select_pool(lagging); inc_queued(pool); From b60f9da4a540853885fcd8753feb0a9d07552631 Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Thu, 16 Aug 2012 17:24:04 +1000 Subject: [PATCH 20/25] Simplify the enough work algorithm dramatically. --- cgminer.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/cgminer.c b/cgminer.c index e6a694bb..548de74b 100644 --- a/cgminer.c +++ b/cgminer.c @@ -2408,9 +2408,13 @@ static void *get_work_thread(void *userdata) ts = __total_staged(); mutex_unlock(stgd_lock); - if (((cs >= opt_queue || cq >= opt_queue) && ts >= maxq) || - ((cs >= opt_queue || cq >= opt_queue) && tq >= maxq && ((cq < maxq && !opt_fail_only) || opt_fail_only)) || - clone_available()) + if (ts >= maxq) + goto out; + + if (ts >= opt_queue && tq >= maxq) + goto out; + + if (clone_available()) goto out; ret_work = make_work(); From b814b42c7f6e8ca8c062eb9c7c4c4c017a9c76ac Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Thu, 16 Aug 2012 18:55:09 +1000 Subject: [PATCH 21/25] Consider us lagging only once our queue is almost full and no staged work. --- cgminer.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/cgminer.c b/cgminer.c index 548de74b..60aea05e 100644 --- a/cgminer.c +++ b/cgminer.c @@ -2411,7 +2411,7 @@ static void *get_work_thread(void *userdata) if (ts >= maxq) goto out; - if (ts >= opt_queue && tq >= maxq) + if (ts >= opt_queue && tq >= maxq - 1) goto out; if (clone_available()) @@ -2426,9 +2426,9 @@ static void *get_work_thread(void *userdata) if (opt_benchmark) get_benchmark_work(ret_work); else { - bool lagging; + bool lagging = false; - if (!ts) + if (ts <= opt_queue) lagging = true; pool = ret_work->pool = select_pool(lagging); inc_queued(pool); @@ -4275,8 +4275,6 @@ void *miner_thread(void *userdata) int64_t hashes_done = 0; int64_t hashes; struct work *work = make_work(); - const time_t request_interval = opt_scantime * 2 / 3 ? : 1; - unsigned const long request_nonce = MAXTHREADS / 3 * 2; bool requested = false; const bool primary = (!mythr->device_thread) || mythr->primary_thread; From e796284cbe6fde6790d3de09aa580e77575aa3a5 Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Thu, 16 Aug 2012 22:26:19 +1000 Subject: [PATCH 22/25] Cope with timeouts and partial reads in ztex code. --- libztex.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/libztex.c b/libztex.c index 636c12c4..95c83f74 100644 --- a/libztex.c +++ b/libztex.c @@ -607,11 +607,19 @@ int libztex_scanDevices(struct libztex_dev_list*** devs_p) int libztex_sendHashData(struct libztex_device *ztex, unsigned char *sendbuf) { - int cnt; + int cnt, ret; if (ztex == NULL || ztex->hndl == NULL) return 0; - cnt = libusb_control_transfer(ztex->hndl, 0x40, 0x80, 0, 0, sendbuf, 44, 1000); + ret = 44; + while (ret > 0) { + cnt = libusb_control_transfer(ztex->hndl, 0x40, 0x80, 0, 0, sendbuf, ret, 1000); + if (cnt >= 0) + ret -= cnt; + else + if (cnt != LIBUSB_ERROR_TIMEOUT) + break; + } if (unlikely(cnt < 0)) applog(LOG_ERR, "%s: Failed sendHashData with err %d", ztex->repr, cnt); @@ -621,7 +629,7 @@ int libztex_sendHashData(struct libztex_device *ztex, unsigned char *sendbuf) int libztex_readHashData(struct libztex_device *ztex, struct libztex_hash_data nonces[]) { int bufsize = 12 + ztex->extraSolutions * 4; unsigned char *rbuf; - int cnt, i, j; + int cnt, i, j, ret; if (ztex->hndl == NULL) return 0; @@ -631,7 +639,16 @@ int libztex_readHashData(struct libztex_device *ztex, struct libztex_hash_data n applog(LOG_ERR, "%s: Failed to allocate memory for reading nonces", ztex->repr); return 0; } - cnt = libusb_control_transfer(ztex->hndl, 0xc0, 0x81, 0, 0, rbuf, bufsize * ztex->numNonces, 1000); + ret = bufsize * ztex->numNonces; + while (ret > 0) { + cnt = libusb_control_transfer(ztex->hndl, 0xc0, 0x81, 0, 0, rbuf, ret, 1000); + if (cnt >= 0) + ret -= cnt; + else + if (cnt != LIBUSB_ERROR_TIMEOUT) + break; + } + if (unlikely(cnt < 0)) { applog(LOG_ERR, "%s: Failed readHashData with err %d", ztex->repr, cnt); free(rbuf); From 0954a229955276e670285a5bc43024edb661e959 Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Thu, 16 Aug 2012 22:30:06 +1000 Subject: [PATCH 23/25] Offset libusb reads/writes by length written as well in ztex. --- libztex.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/libztex.c b/libztex.c index 95c83f74..e4276a37 100644 --- a/libztex.c +++ b/libztex.c @@ -607,16 +607,17 @@ int libztex_scanDevices(struct libztex_dev_list*** devs_p) int libztex_sendHashData(struct libztex_device *ztex, unsigned char *sendbuf) { - int cnt, ret; + int cnt, ret, len; if (ztex == NULL || ztex->hndl == NULL) return 0; - ret = 44; + ret = 44; len = 0; while (ret > 0) { - cnt = libusb_control_transfer(ztex->hndl, 0x40, 0x80, 0, 0, sendbuf, ret, 1000); - if (cnt >= 0) + cnt = libusb_control_transfer(ztex->hndl, 0x40, 0x80, 0, 0, sendbuf + len, ret, 1000); + if (cnt >= 0) { ret -= cnt; - else + len += cnt; + } else if (cnt != LIBUSB_ERROR_TIMEOUT) break; } @@ -628,8 +629,8 @@ int libztex_sendHashData(struct libztex_device *ztex, unsigned char *sendbuf) int libztex_readHashData(struct libztex_device *ztex, struct libztex_hash_data nonces[]) { int bufsize = 12 + ztex->extraSolutions * 4; + int cnt, i, j, ret, len; unsigned char *rbuf; - int cnt, i, j, ret; if (ztex->hndl == NULL) return 0; @@ -639,12 +640,13 @@ int libztex_readHashData(struct libztex_device *ztex, struct libztex_hash_data n applog(LOG_ERR, "%s: Failed to allocate memory for reading nonces", ztex->repr); return 0; } - ret = bufsize * ztex->numNonces; + ret = bufsize * ztex->numNonces; len = 0; while (ret > 0) { - cnt = libusb_control_transfer(ztex->hndl, 0xc0, 0x81, 0, 0, rbuf, ret, 1000); - if (cnt >= 0) + cnt = libusb_control_transfer(ztex->hndl, 0xc0, 0x81, 0, 0, rbuf + len, ret, 1000); + if (cnt >= 0) { ret -= cnt; - else + len += cnt; + } else if (cnt != LIBUSB_ERROR_TIMEOUT) break; } From 9065c63f405eda290bcee5c7b98311fbe93bdb7c Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Fri, 17 Aug 2012 16:42:14 +1000 Subject: [PATCH 24/25] Repeating on timeout in ztex could make the code never return. --- libztex.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/libztex.c b/libztex.c index e4276a37..ae0ee4ae 100644 --- a/libztex.c +++ b/libztex.c @@ -618,8 +618,7 @@ int libztex_sendHashData(struct libztex_device *ztex, unsigned char *sendbuf) ret -= cnt; len += cnt; } else - if (cnt != LIBUSB_ERROR_TIMEOUT) - break; + break; } if (unlikely(cnt < 0)) applog(LOG_ERR, "%s: Failed sendHashData with err %d", ztex->repr, cnt); @@ -647,8 +646,7 @@ int libztex_readHashData(struct libztex_device *ztex, struct libztex_hash_data n ret -= cnt; len += cnt; } else - if (cnt != LIBUSB_ERROR_TIMEOUT) - break; + break; } if (unlikely(cnt < 0)) { From b688d911ea128809ad2878060aa83eb1908330a0 Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Fri, 17 Aug 2012 16:48:02 +1000 Subject: [PATCH 25/25] 0 is a valid return value for read so only break out if read returns -1. --- fpgautils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fpgautils.c b/fpgautils.c index a62b4913..57bd7d5f 100644 --- a/fpgautils.c +++ b/fpgautils.c @@ -252,7 +252,7 @@ _serial_read(int fd, char *buf, size_t bufsiz, char *eol) ssize_t len, tlen = 0; while (bufsiz) { len = read(fd, buf, eol ? 1 : bufsiz); - if (len < 1) + if (unlikely(len == -1)) break; tlen += len; if (eol && *eol == buf[0])