Browse Source

Merge branch 'master' of https://github.com/ckolivas/cgminer.git

Paul Sheppard 13 years ago
parent
commit
df9e76bd73
11 changed files with 358 additions and 204 deletions
  1. 5 0
      adl.c
  2. 5 7
      api.c
  3. 204 90
      cgminer.c
  4. 0 2
      driver-cpu.c
  5. 6 6
      driver-icarus.c
  6. 14 23
      driver-opencl.c
  7. 0 2
      driver-ztex.c
  8. 19 31
      fpgautils.c
  9. 10 10
      miner.h
  10. 84 27
      miner.php
  11. 11 6
      util.c

+ 5 - 0
adl.c

@@ -695,6 +695,11 @@ int gpu_fanpercent(int gpu)
 		applog(LOG_WARNING, "You will need to start cgminer from scratch to correct this");
 		applog(LOG_WARNING, "You will need to start cgminer from scratch to correct this");
 		applog(LOG_WARNING, "Disabling fanspeed monitoring on this device");
 		applog(LOG_WARNING, "Disabling fanspeed monitoring on this device");
 		ga->has_fanspeed = false;
 		ga->has_fanspeed = false;
+		if (ga->twin) {
+			applog(LOG_WARNING, "Disabling fanspeed linking on GPU twins");
+			ga->twin->twin = NULL;;
+			ga->twin = NULL;
+		}
 	}
 	}
 	return ret;
 	return ret;
 }
 }

+ 5 - 7
api.c

@@ -1636,10 +1636,7 @@ static void addpool(__maybe_unused SOCKETTYPE c, char *param, bool isjson)
 		return;
 		return;
 	}
 	}
 
 
-	if (add_pool_details(true, url, user, pass) == ADD_POOL_MAXIMUM) {
-		strcpy(io_buffer, message(MSG_TOOMANYP, MAX_POOLS, NULL, isjson));
-		return;
-	}
+	add_pool_details(true, url, user, pass);
 
 
 	ptr = escape_string(url, isjson);
 	ptr = escape_string(url, isjson);
 	strcpy(io_buffer, message(MSG_ADDPOOL, 0, ptr, isjson));
 	strcpy(io_buffer, message(MSG_ADDPOOL, 0, ptr, isjson));
@@ -2113,12 +2110,13 @@ static int itemstats(int i, char *id, struct cgminer_stats *stats, struct cgmine
 
 
 		if (pool_stats) {
 		if (pool_stats) {
 			sprintf(buf, isjson
 			sprintf(buf, isjson
-				? ",\"Pool Calls\":%d,\"Pool Attempts\":%d,\"Pool Wait\":%ld.%06ld,\"Pool Max\":%ld.%06ld,\"Pool Min\":%ld.%06ld"
-				: ",Pool Calls=%d,Pool Attempts=%d,Pool Wait=%ld.%06ld,Pool Max=%ld.%06ld,Pool Min=%ld.%06ld",
+				? ",\"Pool Calls\":%d,\"Pool Attempts\":%d,\"Pool Wait\":%ld.%06ld,\"Pool Max\":%ld.%06ld,\"Pool Min\":%ld.%06ld,\"Pool Av\":%f"
+				: ",Pool Calls=%d,Pool Attempts=%d,Pool Wait=%ld.%06ld,Pool Max=%ld.%06ld,Pool Min=%ld.%06ld,Pool Av=%f",
 				pool_stats->getwork_calls, pool_stats->getwork_attempts,
 				pool_stats->getwork_calls, pool_stats->getwork_attempts,
 				pool_stats->getwork_wait.tv_sec, pool_stats->getwork_wait.tv_usec,
 				pool_stats->getwork_wait.tv_sec, pool_stats->getwork_wait.tv_usec,
 				pool_stats->getwork_wait_max.tv_sec, pool_stats->getwork_wait_max.tv_usec,
 				pool_stats->getwork_wait_max.tv_sec, pool_stats->getwork_wait_max.tv_usec,
-				pool_stats->getwork_wait_min.tv_sec, pool_stats->getwork_wait_min.tv_usec);
+				pool_stats->getwork_wait_min.tv_sec, pool_stats->getwork_wait_min.tv_usec,
+				pool_stats->getwork_wait_rolling);
 
 
 			strcat(io_buffer, buf);
 			strcat(io_buffer, buf);
 		}
 		}

+ 204 - 90
cgminer.c

@@ -116,7 +116,7 @@ struct list_head scan_devices;
 static signed int devices_enabled;
 static signed int devices_enabled;
 static bool opt_removedisabled;
 static bool opt_removedisabled;
 int total_devices;
 int total_devices;
-struct cgpu_info *devices[MAX_DEVICES];
+struct cgpu_info **devices;
 bool have_opencl;
 bool have_opencl;
 int opt_n_threads = -1;
 int opt_n_threads = -1;
 int mining_threads;
 int mining_threads;
@@ -190,7 +190,7 @@ unsigned int found_blocks;
 unsigned int local_work;
 unsigned int local_work;
 unsigned int total_go, total_ro;
 unsigned int total_go, total_ro;
 
 
-struct pool *pools[MAX_POOLS];
+struct pool **pools;
 static struct pool *currentpool = NULL;
 static struct pool *currentpool = NULL;
 
 
 int total_pools;
 int total_pools;
@@ -395,6 +395,7 @@ static struct pool *add_pool(void)
 	if (!pool)
 	if (!pool)
 		quit(1, "Failed to malloc pool in add_pool");
 		quit(1, "Failed to malloc pool in add_pool");
 	pool->pool_no = pool->prio = total_pools;
 	pool->pool_no = pool->prio = total_pools;
+	pools = realloc(pools, sizeof(struct pool *) * (total_pools + 2));
 	pools[total_pools++] = pool;
 	pools[total_pools++] = pool;
 	if (unlikely(pthread_mutex_init(&pool->pool_lock, NULL)))
 	if (unlikely(pthread_mutex_init(&pool->pool_lock, NULL)))
 		quit(1, "Failed to pthread_mutex_init in add_pool");
 		quit(1, "Failed to pthread_mutex_init in add_pool");
@@ -1622,7 +1623,7 @@ static bool submit_upstream_work(const struct work *work, CURL *curl)
 	int thr_id = work->thr_id;
 	int thr_id = work->thr_id;
 	struct cgpu_info *cgpu = thr_info[thr_id].cgpu;
 	struct cgpu_info *cgpu = thr_info[thr_id].cgpu;
 	struct pool *pool = work->pool;
 	struct pool *pool = work->pool;
-	bool rolltime;
+	int rolltime;
 	uint32_t *hash32;
 	uint32_t *hash32;
 	char hashshow[64+1] = "";
 	char hashshow[64+1] = "";
 
 
@@ -1837,16 +1838,15 @@ static bool get_upstream_work(struct work *work, CURL *curl)
 
 
 	url = pool->rpc_url;
 	url = pool->rpc_url;
 
 
+	gettimeofday(&tv_start, NULL);
 retry:
 retry:
 	/* A single failure response here might be reported as a dead pool and
 	/* A single failure response here might be reported as a dead pool and
 	 * there may be temporary denied messages etc. falsely reporting
 	 * there may be temporary denied messages etc. falsely reporting
 	 * failure so retry a few times before giving up */
 	 * failure so retry a few times before giving up */
 	while (!val && retries++ < 3) {
 	while (!val && retries++ < 3) {
 		pool_stats->getwork_attempts++;
 		pool_stats->getwork_attempts++;
-		gettimeofday(&tv_start, NULL);
 		val = json_rpc_call(curl, url, pool->rpc_userpass, rpc_req,
 		val = json_rpc_call(curl, url, pool->rpc_userpass, rpc_req,
 			    false, false, &work->rolltime, pool, false);
 			    false, false, &work->rolltime, pool, false);
-		gettimeofday(&tv_end, NULL);
 	}
 	}
 	if (unlikely(!val)) {
 	if (unlikely(!val)) {
 		applog(LOG_DEBUG, "Failed json_rpc_call in get_upstream_work");
 		applog(LOG_DEBUG, "Failed json_rpc_call in get_upstream_work");
@@ -1856,12 +1856,12 @@ retry:
 	rc = work_decode(json_object_get(val, "result"), work);
 	rc = work_decode(json_object_get(val, "result"), work);
 	if (!rc && retries < 3)
 	if (!rc && retries < 3)
 		goto retry;
 		goto retry;
-	work->pool = pool;
-	work->longpoll = false;
-	total_getworks++;
-	pool->getwork_requested++;
 
 
+	gettimeofday(&tv_end, NULL);
 	timersub(&tv_end, &tv_start, &tv_elapsed);
 	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;
+	pool_stats->getwork_wait_rolling /= 1.63;
+
 	timeradd(&tv_elapsed, &(pool_stats->getwork_wait), &(pool_stats->getwork_wait));
 	timeradd(&tv_elapsed, &(pool_stats->getwork_wait), &(pool_stats->getwork_wait));
 	if (timercmp(&tv_elapsed, &(pool_stats->getwork_wait_max), >)) {
 	if (timercmp(&tv_elapsed, &(pool_stats->getwork_wait_max), >)) {
 		pool_stats->getwork_wait_max.tv_sec = tv_elapsed.tv_sec;
 		pool_stats->getwork_wait_max.tv_sec = tv_elapsed.tv_sec;
@@ -1873,6 +1873,11 @@ retry:
 	}
 	}
 	pool_stats->getwork_calls++;
 	pool_stats->getwork_calls++;
 
 
+	work->pool = pool;
+	work->longpoll = false;
+	total_getworks++;
+	pool->getwork_requested++;
+
 	json_decref(val);
 	json_decref(val);
 out:
 out:
 
 
@@ -2154,22 +2159,37 @@ static bool workio_get_work(struct workio_cmd *wc)
 static bool stale_work(struct work *work, bool share)
 static bool stale_work(struct work *work, bool share)
 {
 {
 	struct timeval now;
 	struct timeval now;
+	time_t work_expiry;
 	struct pool *pool;
 	struct pool *pool;
+	int getwork_delay;
 
 
 	if (work->mandatory)
 	if (work->mandatory)
 		return false;
 		return false;
 
 
+	if (share)
+		work_expiry = opt_expiry;
+	else if (work->rolltime)
+		work_expiry = work->rolltime;
+	else
+		work_expiry = opt_scantime;
+	pool = work->pool;
+	/* Factor in the average getwork delay of this pool, rounding it up to
+	 * the nearest second */
+	getwork_delay = pool->cgminer_pool_stats.getwork_wait_rolling * 5 + 1;
+	if (!share) {
+		work_expiry -= getwork_delay;
+		if (unlikely(work_expiry < 5))
+			work_expiry = 5;
+	} else
+		work_expiry += getwork_delay;
+
 	gettimeofday(&now, NULL);
 	gettimeofday(&now, NULL);
-	if (share) {
-		if ((now.tv_sec - work->tv_staged.tv_sec) >= opt_expiry)
-			return true;
-	} else if ((now.tv_sec - work->tv_staged.tv_sec) >= opt_scantime)
+	if ((now.tv_sec - work->tv_staged.tv_sec) >= work_expiry)
 		return true;
 		return true;
 
 
 	if (work->work_block != work_block)
 	if (work->work_block != work_block)
 		return true;
 		return true;
 
 
-	pool = work->pool;
 	if (opt_fail_only && !share && pool != current_pool() && pool->enabled != POOL_REJECTING)
 	if (opt_fail_only && !share && pool != current_pool() && pool->enabled != POOL_REJECTING)
 		return true;
 		return true;
 
 
@@ -2379,8 +2399,11 @@ static void inc_queued(void)
 	mutex_unlock(&qd_lock);
 	mutex_unlock(&qd_lock);
 }
 }
 
 
-static void dec_queued(void)
+static void dec_queued(struct work *work)
 {
 {
+	if (work->clone)
+		return;
+
 	mutex_lock(&qd_lock);
 	mutex_lock(&qd_lock);
 	if (total_queued > 0)
 	if (total_queued > 0)
 		total_queued--;
 		total_queued--;
@@ -2397,17 +2420,28 @@ static int requests_queued(void)
 	return ret;
 	return ret;
 }
 }
 
 
-static int discard_stale(void)
+static void subtract_queued(int work_units)
+{
+	mutex_lock(&qd_lock);
+	total_queued -= work_units;
+	if (total_queued < 0)
+		total_queued = 0;
+	mutex_unlock(&qd_lock);
+}
+
+static void discard_stale(void)
 {
 {
 	struct work *work, *tmp;
 	struct work *work, *tmp;
-	int i, stale = 0;
+	int stale = 0, nonclone = 0;
 
 
 	mutex_lock(stgd_lock);
 	mutex_lock(stgd_lock);
 	HASH_ITER(hh, staged_work, work, tmp) {
 	HASH_ITER(hh, staged_work, work, tmp) {
 		if (stale_work(work, false)) {
 		if (stale_work(work, false)) {
 			HASH_DEL(staged_work, work);
 			HASH_DEL(staged_work, work);
-			if (work->clone || work->longpoll)
+			if (work->clone)
 				--staged_extras;
 				--staged_extras;
+			else
+				nonclone++;
 			discard_work(work);
 			discard_work(work);
 			stale++;
 			stale++;
 		}
 		}
@@ -2417,23 +2451,19 @@ static int discard_stale(void)
 	applog(LOG_DEBUG, "Discarded %d stales that didn't match current hash", stale);
 	applog(LOG_DEBUG, "Discarded %d stales that didn't match current hash", stale);
 
 
 	/* Dec queued outside the loop to not have recursive locks */
 	/* Dec queued outside the loop to not have recursive locks */
-	for (i = 0; i < stale; i++)
-		dec_queued();
-
-	return stale;
+	subtract_queued(nonclone);
 }
 }
 
 
 static bool queue_request(struct thr_info *thr, bool needed);
 static bool queue_request(struct thr_info *thr, bool needed);
 
 
 static void restart_threads(void)
 static void restart_threads(void)
 {
 {
-	int i, stale;
+	int i;
 
 
 	/* Discard staged work that is now stale */
 	/* Discard staged work that is now stale */
-	stale = discard_stale();
+	discard_stale();
 
 
-	for (i = 0; i < stale; i++)
-		queue_request(NULL, true);
+	queue_request(NULL, true);
 
 
 	for (i = 0; i < mining_threads; i++)
 	for (i = 0; i < mining_threads; i++)
 		work_restart[i].restart = 1;
 		work_restart[i].restart = 1;
@@ -2556,7 +2586,7 @@ static bool hash_push(struct work *work)
 	if (likely(!getq->frozen)) {
 	if (likely(!getq->frozen)) {
 		HASH_ADD_INT(staged_work, id, work);
 		HASH_ADD_INT(staged_work, id, work);
 		HASH_SORT(staged_work, tv_sort);
 		HASH_SORT(staged_work, tv_sort);
-		if (work->clone || work->longpoll)
+		if (work->clone)
 			++staged_extras;
 			++staged_extras;
 	} else
 	} else
 		rc = false;
 		rc = false;
@@ -3385,7 +3415,7 @@ static bool pool_active(struct pool *pool, bool pinging)
 	bool ret = false;
 	bool ret = false;
 	json_t *val;
 	json_t *val;
 	CURL *curl;
 	CURL *curl;
-	bool rolltime;
+	int rolltime;
 
 
 	curl = curl_easy_init();
 	curl = curl_easy_init();
 	if (unlikely(!curl)) {
 	if (unlikely(!curl)) {
@@ -3493,39 +3523,78 @@ static void pool_resus(struct pool *pool)
 		switch_pools(NULL);
 		switch_pools(NULL);
 }
 }
 
 
-static long requested_tv_sec;
+static time_t requested_tv_sec;
+
+static bool control_tset(bool *var)
+{
+	bool ret;
+
+	mutex_lock(&control_lock);
+	ret = *var;
+	*var = true;
+	mutex_unlock(&control_lock);
+
+	return ret;
+}
+
+static void control_tclear(bool *var)
+{
+	mutex_lock(&control_lock);
+	*var = false;
+	mutex_unlock(&control_lock);
+}
+
+static bool queueing;
 
 
 static bool queue_request(struct thr_info *thr, bool needed)
 static bool queue_request(struct thr_info *thr, bool needed)
 {
 {
-	int rq = requests_queued();
 	struct workio_cmd *wc;
 	struct workio_cmd *wc;
 	struct timeval now;
 	struct timeval now;
+	time_t scan_post;
+	int rq, rs;
+	bool ret = true;
+
+	/* Prevent multiple requests being executed at once */
+	if (control_tset(&queueing))
+		return ret;
+
+	rq = requests_queued();
+	rs = requests_staged();
+
+	/* Grab more work every 2/3 of the scan time to avoid all work expiring
+	 * at the same time */
+	scan_post = opt_scantime * 2 / 3;
+	if (scan_post < 5)
+		scan_post = 5;
 
 
 	gettimeofday(&now, NULL);
 	gettimeofday(&now, NULL);
 
 
-	/* Space out retrieval of extra work according to the number of mining
-	 * threads */
-	if (rq >= mining_threads + staged_extras &&
-	    (now.tv_sec - requested_tv_sec) < opt_scantime / (mining_threads + 1))
-		return true;
+	/* Test to make sure we have enough work for pools without rolltime
+	 * and enough original work for pools with rolltime */
+	if ((rq >= mining_threads || rs >= mining_threads) &&
+	    rq > staged_extras + opt_queue &&
+	    now.tv_sec - requested_tv_sec < scan_post)
+		goto out;
+
+	requested_tv_sec = now.tv_sec;
+
+	inc_queued();
 
 
 	/* fill out work request message */
 	/* fill out work request message */
 	wc = calloc(1, sizeof(*wc));
 	wc = calloc(1, sizeof(*wc));
 	if (unlikely(!wc)) {
 	if (unlikely(!wc)) {
 		applog(LOG_ERR, "Failed to calloc wc in queue_request");
 		applog(LOG_ERR, "Failed to calloc wc in queue_request");
-		return false;
+		ret = false;
+		goto out;
 	}
 	}
 
 
 	wc->cmd = WC_GET_WORK;
 	wc->cmd = WC_GET_WORK;
-	if (thr)
-		wc->thr = thr;
-	else
-		wc->thr = NULL;
+	wc->thr = thr;
 
 
 	/* If we're queueing work faster than we can stage it, consider the
 	/* 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
 	 * system lagging and allow work to be gathered from another pool if
 	 * possible */
 	 * possible */
-	if (rq && needed && !requests_staged() && !opt_fail_only)
+	if (rq && needed && !rs && !opt_fail_only)
 		wc->lagging = true;
 		wc->lagging = true;
 
 
 	applog(LOG_DEBUG, "Queueing getwork request to work thread");
 	applog(LOG_DEBUG, "Queueing getwork request to work thread");
@@ -3534,12 +3603,13 @@ static bool queue_request(struct thr_info *thr, bool needed)
 	if (unlikely(!tq_push(thr_info[work_thr_id].q, wc))) {
 	if (unlikely(!tq_push(thr_info[work_thr_id].q, wc))) {
 		applog(LOG_ERR, "Failed to tq_push in queue_request");
 		applog(LOG_ERR, "Failed to tq_push in queue_request");
 		workio_cmd_free(wc);
 		workio_cmd_free(wc);
-		return false;
+		ret = false;
 	}
 	}
 
 
-	requested_tv_sec = now.tv_sec;
-	inc_queued();
-	return true;
+out:
+	control_tclear(&queueing);
+
+	return ret;
 }
 }
 
 
 static struct work *hash_pop(const struct timespec *abstime)
 static struct work *hash_pop(const struct timespec *abstime)
@@ -3554,7 +3624,7 @@ static struct work *hash_pop(const struct timespec *abstime)
 	if (HASH_COUNT(staged_work)) {
 	if (HASH_COUNT(staged_work)) {
 		work = staged_work;
 		work = staged_work;
 		HASH_DEL(staged_work, work);
 		HASH_DEL(staged_work, work);
-		if (work->clone || work->longpoll)
+		if (work->clone)
 			--staged_extras;
 			--staged_extras;
 	}
 	}
 	mutex_unlock(stgd_lock);
 	mutex_unlock(stgd_lock);
@@ -3571,8 +3641,7 @@ static inline bool should_roll(struct work *work)
 
 
 static inline bool can_roll(struct work *work)
 static inline bool can_roll(struct work *work)
 {
 {
-	return (work->pool && !stale_work(work, false) && work->rolltime &&
-		work->rolls < 11 && !work->clone);
+	return (work->pool && !stale_work(work, false) && work->rolltime && !work->clone);
 }
 }
 
 
 static void roll_work(struct work *work)
 static void roll_work(struct work *work)
@@ -3603,6 +3672,58 @@ static bool reuse_work(struct work *work)
 	return false;
 	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;
+	/* 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 */
+static struct work *clone_work(struct work *work)
+{
+	int mrs = mining_threads - requests_staged();
+	struct work *work_clone;
+	bool cloned;
+
+	if (mrs < 1)
+		return work;
+
+	cloned = false;
+	work_clone = make_clone(work);
+	while (mrs-- > 0 && can_roll(work) && should_roll(work)) {
+		applog(LOG_DEBUG, "Pushing rolled converted work to stage thread");
+		if (unlikely(!stage_work(work_clone))) {
+			cloned = false;
+			break;
+		}
+		roll_work(work);
+		work_clone = make_clone(work);
+		/* Roll it again to prevent duplicates should this be used
+		 * directly later on */
+		roll_work(work);
+		cloned = true;
+	}
+
+	if (cloned) {
+		stage_work(work);
+		return work_clone;
+	}
+
+	free_work(work_clone);
+
+	return work;
+}
+
 static bool get_work(struct work *work, bool requested, struct thr_info *thr,
 static bool get_work(struct work *work, bool requested, struct thr_info *thr,
 		     const int thr_id)
 		     const int thr_id)
 {
 {
@@ -3674,7 +3795,7 @@ retry:
 	}
 	}
 
 
 	if (stale_work(work_heap, false)) {
 	if (stale_work(work_heap, false)) {
-		dec_queued();
+		dec_queued(work_heap);
 		discard_work(work_heap);
 		discard_work(work_heap);
 		goto retry;
 		goto retry;
 	}
 	}
@@ -3687,18 +3808,10 @@ retry:
 			pool_resus(pool);
 			pool_resus(pool);
 	}
 	}
 
 
-	memcpy(work, work_heap, sizeof(*work));
-
-	/* Hand out a clone if we can roll this work item */
-	if (reuse_work(work_heap)) {
-		applog(LOG_DEBUG, "Pushing divided work to get queue head");
-
-		stage_work(work_heap);
-		work->clone = true;
-	} else {
-		dec_queued();
-		free_work(work_heap);
-	}
+	work_heap = clone_work(work_heap);
+	memcpy(work, work_heap, sizeof(struct work));
+	dec_queued(work_heap);
+	free_work(work_heap);
 
 
 	ret = true;
 	ret = true;
 out:
 out:
@@ -4023,9 +4136,9 @@ enum {
 };
 };
 
 
 /* Stage another work item from the work returned in a longpoll */
 /* Stage another work item from the work returned in a longpoll */
-static void convert_to_work(json_t *val, bool rolltime, struct pool *pool)
+static void convert_to_work(json_t *val, int rolltime, struct pool *pool)
 {
 {
-	struct work *work, *work_clone;
+	struct work *work;
 	bool rc;
 	bool rc;
 
 
 	work = make_work();
 	work = make_work();
@@ -4058,25 +4171,16 @@ static void convert_to_work(json_t *val, bool rolltime, struct pool *pool)
 		return;
 		return;
 	}
 	}
 
 
-	work_clone = make_work();
-	memcpy(work_clone, work, sizeof(struct work));
-	while (reuse_work(work)) {
-		work_clone->clone = true;
-		work_clone->longpoll = false;
-		applog(LOG_DEBUG, "Pushing rolled converted work to stage thread");
-		if (unlikely(!stage_work(work_clone)))
-			break;
-		work_clone = make_work();
-		memcpy(work_clone, work, sizeof(struct work));
-	}
-	free_work(work_clone);
+	work = clone_work(work);
 
 
 	applog(LOG_DEBUG, "Pushing converted work to stage thread");
 	applog(LOG_DEBUG, "Pushing converted work to stage thread");
 
 
 	if (unlikely(!stage_work(work)))
 	if (unlikely(!stage_work(work)))
 		free_work(work);
 		free_work(work);
-	else
+	else {
+		inc_queued();
 		applog(LOG_DEBUG, "Converted longpoll data to work");
 		applog(LOG_DEBUG, "Converted longpoll data to work");
+	}
 }
 }
 
 
 /* If we want longpoll, enable it for the chosen default pool, or, if
 /* If we want longpoll, enable it for the chosen default pool, or, if
@@ -4121,7 +4225,7 @@ static void *longpoll_thread(void *userdata)
 	struct timeval start, end;
 	struct timeval start, end;
 	CURL *curl = NULL;
 	CURL *curl = NULL;
 	int failures = 0;
 	int failures = 0;
-	bool rolltime;
+	int rolltime;
 
 
 	curl = curl_easy_init();
 	curl = curl_easy_init();
 	if (unlikely(!curl)) {
 	if (unlikely(!curl)) {
@@ -4272,6 +4376,23 @@ static void *watchpool_thread(void __maybe_unused *userdata)
 	return NULL;
 	return NULL;
 }
 }
 
 
+/* Work is sorted according to age, so discard the oldest work items, leaving
+ * only 1 staged work item per mining thread */
+static void age_work(void)
+{
+	int discarded = 0;
+
+	while (requests_staged() > mining_threads * 4 / 3 + opt_queue) {
+		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
 /* 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
  * the screen at regular intervals, and restarts threads if they appear to have
@@ -4294,6 +4415,8 @@ static void *watchdog_thread(void __maybe_unused *userdata)
 		if (requests_queued() < opt_queue)
 		if (requests_queued() < opt_queue)
 			queue_request(NULL, false);
 			queue_request(NULL, false);
 
 
+		age_work();
+
 		hashmeter(-1, &zero_tv, 0);
 		hashmeter(-1, &zero_tv, 0);
 
 
 #ifdef HAVE_CURSES
 #ifdef HAVE_CURSES
@@ -4581,13 +4704,10 @@ char *curses_input(const char *query)
 }
 }
 #endif
 #endif
 
 
-int add_pool_details(bool live, char *url, char *user, char *pass)
+void add_pool_details(bool live, char *url, char *user, char *pass)
 {
 {
 	struct pool *pool;
 	struct pool *pool;
 
 
-	if (total_pools == MAX_POOLS)
-		return ADD_POOL_MAXIMUM;
-
 	pool = add_pool();
 	pool = add_pool();
 
 
 	pool->rpc_url = url;
 	pool->rpc_url = url;
@@ -4603,8 +4723,6 @@ int add_pool_details(bool live, char *url, char *user, char *pass)
 	pool->enabled = POOL_ENABLED;
 	pool->enabled = POOL_ENABLED;
 	if (live && !pool_active(pool, false))
 	if (live && !pool_active(pool, false))
 		pool->idle = true;
 		pool->idle = true;
-
-	return ADD_POOL_OK;
 }
 }
 
 
 #ifdef HAVE_CURSES
 #ifdef HAVE_CURSES
@@ -4614,10 +4732,6 @@ static bool input_pool(bool live)
 	bool ret = false;
 	bool ret = false;
 
 
 	immedok(logwin, true);
 	immedok(logwin, true);
-	if (total_pools == MAX_POOLS) {
-		wlogprint("Reached maximum number of pools.\n");
-		goto out;
-	}
 	wlogprint("Input server details.\n");
 	wlogprint("Input server details.\n");
 
 
 	url = curses_input("URL");
 	url = curses_input("URL");
@@ -4645,7 +4759,8 @@ static bool input_pool(bool live)
 	if (!pass)
 	if (!pass)
 		goto out;
 		goto out;
 
 
-	ret = (add_pool_details(live, url, user, pass) == ADD_POOL_OK);
+	add_pool_details(live, url, user, pass);
+	ret = true;
 out:
 out:
 	immedok(logwin, false);
 	immedok(logwin, false);
 
 
@@ -4815,6 +4930,7 @@ bool add_cgpu(struct cgpu_info*cgpu)
 		cgpu->device_id = d->lastid = 0;
 		cgpu->device_id = d->lastid = 0;
 		HASH_ADD_STR(devids, name, d);
 		HASH_ADD_STR(devids, name, d);
 	}
 	}
+	devices = realloc(devices, sizeof(struct cgpu_info *) * (total_devices + 2));
 	devices[total_devices++] = cgpu;
 	devices[total_devices++] = cgpu;
 	return true;
 	return true;
 }
 }
@@ -4904,8 +5020,6 @@ int main(int argc, char *argv[])
 		gpus[i].dynamic = true;
 		gpus[i].dynamic = true;
 #endif
 #endif
 
 
-	memset(devices, 0, sizeof(devices));
-
 	/* parse command line */
 	/* parse command line */
 	opt_register_table(opt_config_table,
 	opt_register_table(opt_config_table,
 			   "Options for both config file and command line");
 			   "Options for both config file and command line");

+ 0 - 2
driver-cpu.c

@@ -731,8 +731,6 @@ static void cpu_detect()
 	if (num_processors < 1)
 	if (num_processors < 1)
 		return;
 		return;
 
 
-	if (total_devices + opt_n_threads > MAX_DEVICES)
-		opt_n_threads = MAX_DEVICES - total_devices;
 	cpus = calloc(opt_n_threads, sizeof(struct cgpu_info));
 	cpus = calloc(opt_n_threads, sizeof(struct cgpu_info));
 	if (unlikely(!cpus))
 	if (unlikely(!cpus))
 		quit(1, "Failed to calloc cpus");
 		quit(1, "Failed to calloc cpus");

+ 6 - 6
driver-icarus.c

@@ -179,7 +179,7 @@ struct ICARUS_INFO {
 };
 };
 
 
 // One for each possible device
 // One for each possible device
-static struct ICARUS_INFO *icarus_info[MAX_DEVICES];
+static struct ICARUS_INFO **icarus_info;
 
 
 struct device_api icarus_api;
 struct device_api icarus_api;
 
 
@@ -421,15 +421,15 @@ static bool icarus_detect_one(const char *devpath)
 	icarus->device_path = strdup(devpath);
 	icarus->device_path = strdup(devpath);
 	icarus->threads = 1;
 	icarus->threads = 1;
 	add_cgpu(icarus);
 	add_cgpu(icarus);
+	icarus_info = realloc(icarus_info, sizeof(struct ICARUS_INFO *) * (total_devices + 1));
 
 
 	applog(LOG_INFO, "Found Icarus at %s, mark as %d",
 	applog(LOG_INFO, "Found Icarus at %s, mark as %d",
 		devpath, icarus->device_id);
 		devpath, icarus->device_id);
 
 
-	if (icarus_info[icarus->device_id] == NULL) {
-		icarus_info[icarus->device_id] = (struct ICARUS_INFO *)malloc(sizeof(struct ICARUS_INFO));
-		if (unlikely(!(icarus_info[icarus->device_id])))
-			quit(1, "Failed to malloc ICARUS_INFO");
-	}
+	// Since we are adding a new device on the end it needs to always be allocated
+	icarus_info[icarus->device_id] = (struct ICARUS_INFO *)malloc(sizeof(struct ICARUS_INFO));
+	if (unlikely(!(icarus_info[icarus->device_id])))
+		quit(1, "Failed to malloc ICARUS_INFO");
 
 
 	info = icarus_info[icarus->device_id];
 	info = icarus_info[icarus->device_id];
 
 

+ 14 - 23
driver-opencl.c

@@ -538,15 +538,11 @@ struct cgpu_info *cpus;
 void pause_dynamic_threads(int gpu)
 void pause_dynamic_threads(int gpu)
 {
 {
 	struct cgpu_info *cgpu = &gpus[gpu];
 	struct cgpu_info *cgpu = &gpus[gpu];
-	int i, thread_no = 0;
+	int i;
 
 
-	for (i = 0; i < mining_threads; i++) {
+	for (i = 1; i < cgpu->threads; i++) {
 		struct thr_info *thr = &thr_info[i];
 		struct thr_info *thr = &thr_info[i];
 
 
-		if (thr->cgpu != cgpu)
-			continue;
-		if (!thread_no++)
-			continue;
 		if (!thr->pause && cgpu->dynamic) {
 		if (!thr->pause && cgpu->dynamic) {
 			applog(LOG_WARNING, "Disabling extra threads due to dynamic mode.");
 			applog(LOG_WARNING, "Disabling extra threads due to dynamic mode.");
 			applog(LOG_WARNING, "Tune dynamic intensity with --gpu-dyninterval");
 			applog(LOG_WARNING, "Tune dynamic intensity with --gpu-dyninterval");
@@ -1130,9 +1126,6 @@ static void opencl_detect()
 		nDevs = 0;
 		nDevs = 0;
 	}
 	}
 
 
-	if (MAX_DEVICES - total_devices < nDevs)
-		nDevs = MAX_DEVICES - total_devices;
-
 	if (!nDevs)
 	if (!nDevs)
 		return;
 		return;
 
 
@@ -1354,34 +1347,32 @@ static uint64_t opencl_scanhash(struct thr_info *thr, struct work *work,
 	_clState *clState = clStates[thr_id];
 	_clState *clState = clStates[thr_id];
 	const cl_kernel *kernel = &clState->kernel;
 	const cl_kernel *kernel = &clState->kernel;
 
 
-	double gpu_ms_average = 7;
 	cl_int status;
 	cl_int status;
-
 	size_t globalThreads[1];
 	size_t globalThreads[1];
 	size_t localThreads[1] = { clState->wsize };
 	size_t localThreads[1] = { clState->wsize };
 	unsigned int threads;
 	unsigned int threads;
 	unsigned int hashes;
 	unsigned int hashes;
 
 
-
-	struct timeval tv_gpustart, tv_gpuend, diff;
-	suseconds_t gpu_us;
-
-	gettimeofday(&tv_gpustart, NULL);
-	timeval_subtract(&diff, &tv_gpustart, &tv_gpuend);
+	gettimeofday(&gpu->tv_gpustart, NULL);
 	/* This finish flushes the readbuffer set with CL_FALSE later */
 	/* This finish flushes the readbuffer set with CL_FALSE later */
 	clFinish(clState->commandQueue);
 	clFinish(clState->commandQueue);
-	gettimeofday(&tv_gpuend, NULL);
-	timeval_subtract(&diff, &tv_gpuend, &tv_gpustart);
-	gpu_us = diff.tv_sec * 1000000 + diff.tv_usec;
-	decay_time(&gpu_ms_average, gpu_us / 1000);
+	gettimeofday(&gpu->tv_gpuend, NULL);
+
 	if (gpu->dynamic) {
 	if (gpu->dynamic) {
+		struct timeval diff;
+		suseconds_t gpu_ms;
+
+		timersub(&gpu->tv_gpuend, &gpu->tv_gpustart, &diff);
+		gpu_ms = diff.tv_sec * 1000 + diff.tv_usec / 1000;
+		gpu->gpu_ms_average = (gpu->gpu_ms_average + gpu_ms * 0.63) / 1.63;
+
 		/* Try to not let the GPU be out for longer than 6ms, but
 		/* Try to not let the GPU be out for longer than 6ms, but
 		 * increase intensity when the system is idle, unless
 		 * increase intensity when the system is idle, unless
 		 * dynamic is disabled. */
 		 * dynamic is disabled. */
-		if (gpu_ms_average > opt_dynamic_interval) {
+		if (gpu->gpu_ms_average > opt_dynamic_interval) {
 			if (gpu->intensity > MIN_INTENSITY)
 			if (gpu->intensity > MIN_INTENSITY)
 				--gpu->intensity;
 				--gpu->intensity;
-		} else if (gpu_ms_average < ((opt_dynamic_interval / 2) ? : 1)) {
+		} else if (gpu->gpu_ms_average < ((opt_dynamic_interval / 2) ? : 1)) {
 			if (gpu->intensity < MAX_INTENSITY)
 			if (gpu->intensity < MAX_INTENSITY)
 				++gpu->intensity;
 				++gpu->intensity;
 		}
 		}

+ 0 - 2
driver-ztex.c

@@ -66,8 +66,6 @@ static void ztex_detect(void)
 	applog(LOG_WARNING, "Found %d ztex board(s)", cnt);
 	applog(LOG_WARNING, "Found %d ztex board(s)", cnt);
 
 
 	for (i = 0; i < cnt; i++) {
 	for (i = 0; i < cnt; i++) {
-		if (total_devices == MAX_DEVICES)
-			break;
 		ztex = calloc(1, sizeof(struct cgpu_info));
 		ztex = calloc(1, sizeof(struct cgpu_info));
 		ztex->api = &ztex_api;
 		ztex->api = &ztex_api;
 		ztex->device_ztex = ztex_devices[i]->dev;
 		ztex->device_ztex = ztex_devices[i]->dev;

+ 19 - 31
fpgautils.c

@@ -40,9 +40,6 @@
 char
 char
 serial_autodetect_udev(detectone_func_t detectone, const char*prodname)
 serial_autodetect_udev(detectone_func_t detectone, const char*prodname)
 {
 {
-	if (total_devices == MAX_DEVICES)
-		return 0;
-
 	struct udev *udev = udev_new();
 	struct udev *udev = udev_new();
 	struct udev_enumerate *enumerate = udev_enumerate_new(udev);
 	struct udev_enumerate *enumerate = udev_enumerate_new(udev);
 	struct udev_list_entry *list_entry;
 	struct udev_list_entry *list_entry;
@@ -64,9 +61,6 @@ serial_autodetect_udev(detectone_func_t detectone, const char*prodname)
 			++found;
 			++found;
 
 
 		udev_device_unref(device);
 		udev_device_unref(device);
-
-		if (total_devices == MAX_DEVICES)
-			break;
 	}
 	}
 	udev_enumerate_unref(enumerate);
 	udev_enumerate_unref(enumerate);
 	udev_unref(udev);
 	udev_unref(udev);
@@ -85,9 +79,6 @@ char
 serial_autodetect_devserial(detectone_func_t detectone, const char*prodname)
 serial_autodetect_devserial(detectone_func_t detectone, const char*prodname)
 {
 {
 #ifndef WIN32
 #ifndef WIN32
-	if (total_devices == MAX_DEVICES)
-		return 0;
-
 	DIR *D;
 	DIR *D;
 	struct dirent *de;
 	struct dirent *de;
 	const char udevdir[] = "/dev/serial/by-id";
 	const char udevdir[] = "/dev/serial/by-id";
@@ -104,11 +95,8 @@ serial_autodetect_devserial(detectone_func_t detectone, const char*prodname)
 		if (!strstr(de->d_name, prodname))
 		if (!strstr(de->d_name, prodname))
 			continue;
 			continue;
 		strcpy(devfile, de->d_name);
 		strcpy(devfile, de->d_name);
-		if (detectone(devpath)) {
+		if (detectone(devpath))
 			++found;
 			++found;
-			if (total_devices == MAX_DEVICES)
-				break;
-		}
 	}
 	}
 	closedir(D);
 	closedir(D);
 
 
@@ -121,9 +109,6 @@ serial_autodetect_devserial(detectone_func_t detectone, const char*prodname)
 char
 char
 _serial_detect(const char*dname, detectone_func_t detectone, autoscan_func_t autoscan, bool forceauto)
 _serial_detect(const char*dname, detectone_func_t detectone, autoscan_func_t autoscan, bool forceauto)
 {
 {
-	if (total_devices == MAX_DEVICES)
-		return 0;
-
 	struct string_elist *iter, *tmp;
 	struct string_elist *iter, *tmp;
 	const char*s, *p;
 	const char*s, *p;
 	bool inhibitauto = false;
 	bool inhibitauto = false;
@@ -148,12 +133,10 @@ _serial_detect(const char*dname, detectone_func_t detectone, autoscan_func_t aut
 			string_elist_del(iter);
 			string_elist_del(iter);
 			inhibitauto = true;
 			inhibitauto = true;
 			++found;
 			++found;
-			if (total_devices == MAX_DEVICES)
-				break;
 		}
 		}
 	}
 	}
 
 
-	if ((forceauto || !inhibitauto) && autoscan && total_devices < MAX_DEVICES)
+	if ((forceauto || !inhibitauto) && autoscan)
 		found += autoscan();
 		found += autoscan();
 
 
 	return found;
 	return found;
@@ -198,28 +181,33 @@ serial_open(const char*devpath, unsigned long baud, signed short timeout, bool p
 	if (unlikely(fdDev == -1))
 	if (unlikely(fdDev == -1))
 		return -1;
 		return -1;
 
 
-	struct termios pattr;
-	tcgetattr(fdDev, &pattr);
-	pattr.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
-	pattr.c_oflag &= ~OPOST;
-	pattr.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
-	pattr.c_cflag &= ~(CSIZE | PARENB);
-	pattr.c_cflag |= CS8;
+	struct termios my_termios;
+
+	tcgetattr(fdDev, &my_termios);
 
 
 	switch (baud) {
 	switch (baud) {
 	case 0: break;
 	case 0: break;
-	case 115200: pattr.c_cflag = B115200; break;
+	case 115200: my_termios.c_cflag = B115200; break;
 	default:
 	default:
 		applog(LOG_WARNING, "Unrecognized baud rate: %lu", baud);
 		applog(LOG_WARNING, "Unrecognized baud rate: %lu", baud);
 	}
 	}
-	pattr.c_cflag |= CREAD | CLOCAL;
+
+	my_termios.c_cflag |= CS8;
+	my_termios.c_cflag |= CREAD;
+	my_termios.c_cflag |= CLOCAL;
+	my_termios.c_cflag &= ~(CSIZE | PARENB);
+
+	my_termios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK |
+				ISTRIP | INLCR | IGNCR | ICRNL | IXON);
+	my_termios.c_oflag &= ~OPOST;
+	my_termios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
 
 
 	if (timeout >= 0) {
 	if (timeout >= 0) {
-		pattr.c_cc[VTIME] = (cc_t)timeout;
-		pattr.c_cc[VMIN] = 0;
+		my_termios.c_cc[VTIME] = (cc_t)timeout;
+		my_termios.c_cc[VMIN] = 0;
 	}
 	}
 
 
-	tcsetattr(fdDev, TCSANOW, &pattr);
+	tcsetattr(fdDev, TCSANOW, &my_termios);
 	if (purge)
 	if (purge)
 		tcflush(fdDev, TCIOFLUSH);
 		tcflush(fdDev, TCIOFLUSH);
 	return fdDev;
 	return fdDev;

+ 10 - 10
miner.h

@@ -300,6 +300,7 @@ struct cgminer_pool_stats {
 	struct timeval getwork_wait;
 	struct timeval getwork_wait;
 	struct timeval getwork_wait_max;
 	struct timeval getwork_wait_max;
 	struct timeval getwork_wait_min;
 	struct timeval getwork_wait_min;
+	double getwork_wait_rolling;
 };
 };
 
 
 struct cgpu_info {
 struct cgpu_info {
@@ -347,6 +348,10 @@ struct cgpu_info {
 	cl_uint vwidth;
 	cl_uint vwidth;
 	size_t work_size;
 	size_t work_size;
 	enum cl_kernels kernel;
 	enum cl_kernels kernel;
+
+	struct timeval tv_gpustart;;
+	struct timeval tv_gpuend;
+	double gpu_ms_average;
 #endif
 #endif
 
 
 	float temp;
 	float temp;
@@ -537,7 +542,7 @@ extern pthread_rwlock_t netacc_lock;
 
 
 extern const uint32_t sha256_init_state[];
 extern const uint32_t sha256_init_state[];
 extern json_t *json_rpc_call(CURL *curl, const char *url, const char *userpass,
 extern json_t *json_rpc_call(CURL *curl, const char *url, const char *userpass,
-			     const char *rpc_req, bool, bool, bool *,
+			     const char *rpc_req, bool, bool, int *,
 			     struct pool *pool, bool);
 			     struct pool *pool, bool);
 extern char *bin2hex(const unsigned char *p, size_t len);
 extern char *bin2hex(const unsigned char *p, size_t len);
 extern bool hex2bin(unsigned char *p, const char *hexstr, size_t len);
 extern bool hex2bin(unsigned char *p, const char *hexstr, size_t len);
@@ -580,14 +585,9 @@ extern void api(int thr_id);
 
 
 extern struct pool *current_pool(void);
 extern struct pool *current_pool(void);
 extern int active_pools(void);
 extern int active_pools(void);
-extern int add_pool_details(bool live, char *url, char *user, char *pass);
-
-#define ADD_POOL_MAXIMUM 1
-#define ADD_POOL_OK 0
+extern void add_pool_details(bool live, char *url, char *user, char *pass);
 
 
 #define MAX_GPUDEVICES 16
 #define MAX_GPUDEVICES 16
-#define MAX_DEVICES 64
-#define MAX_POOLS (32)
 
 
 #define MIN_INTENSITY -10
 #define MIN_INTENSITY -10
 #define _MIN_INTENSITY_STR "-10"
 #define _MIN_INTENSITY_STR "-10"
@@ -608,9 +608,9 @@ extern double total_secs;
 extern int mining_threads;
 extern int mining_threads;
 extern struct cgpu_info *cpus;
 extern struct cgpu_info *cpus;
 extern int total_devices;
 extern int total_devices;
-extern struct cgpu_info *devices[];
+extern struct cgpu_info **devices;
 extern int total_pools;
 extern int total_pools;
-extern struct pool *pools[MAX_POOLS];
+extern struct pool **pools;
 extern const char *algo_names[];
 extern const char *algo_names[];
 extern enum sha256_algos opt_algo;
 extern enum sha256_algos opt_algo;
 extern struct strategies strategies[];
 extern struct strategies strategies[];
@@ -739,7 +739,7 @@ struct work {
 	bool		mined;
 	bool		mined;
 	bool		clone;
 	bool		clone;
 	bool		cloned;
 	bool		cloned;
-	bool		rolltime;
+	int		rolltime;
 	bool		longpoll;
 	bool		longpoll;
 	bool		stale;
 	bool		stale;
 	bool		mandatory;
 	bool		mandatory;

+ 84 - 27
miner.php

@@ -3,6 +3,7 @@ session_start();
 #
 #
 global $miner, $port, $readonly, $notify, $rigs, $socktimeoutsec;
 global $miner, $port, $readonly, $notify, $rigs, $socktimeoutsec;
 global $checklastshare, $hidefields;
 global $checklastshare, $hidefields;
+global $ignorerefresh, $changerefresh, $autorefresh;
 #
 #
 # Don't touch these 2 - see $rigs below
 # Don't touch these 2 - see $rigs below
 $miner = null;
 $miner = null;
@@ -50,6 +51,14 @@ $socktimeoutsec = 10;
 #$hidefields = array('POOL.URL' => 1, 'POOL.User' => 1);
 #$hidefields = array('POOL.URL' => 1, 'POOL.User' => 1);
 $hidefields = array();
 $hidefields = array();
 #
 #
+# Auto-refresh of the page (in seconds)
+# $ignorerefresh = true/false always ignore refresh parameters
+# $changerefresh = true/false show buttons to change the value
+# $autorefresh = default value, 0 means dont auto-refresh
+$ignorerefresh = false;
+$changerefresh = true;
+$autorefresh = 0;
+#
 $here = $_SERVER['PHP_SELF'];
 $here = $_SERVER['PHP_SELF'];
 #
 #
 global $tablebegin, $tableend, $warnfont, $warnoff, $dfmt;
 global $tablebegin, $tableend, $warnfont, $warnoff, $dfmt;
@@ -60,6 +69,11 @@ $warnfont = '<font color=red><b>';
 $warnoff = '</b></font>';
 $warnoff = '</b></font>';
 $dfmt = 'H:i:s j-M-Y \U\T\CP';
 $dfmt = 'H:i:s j-M-Y \U\T\CP';
 #
 #
+global $miner_font_family, $miner_font_size;
+#
+$miner_font_family = 'verdana,arial,sans';
+$miner_font_size = '13pt';
+#
 # This below allows you to put your own settings into a seperate file
 # This below allows you to put your own settings into a seperate file
 # so you don't need to update miner.php with your preferred settings
 # so you don't need to update miner.php with your preferred settings
 # every time a new version is released
 # every time a new version is released
@@ -76,9 +90,24 @@ $showndate = false;
 global $rigerror;
 global $rigerror;
 $rigerror = array();
 $rigerror = array();
 #
 #
-function htmlhead($checkapi)
+function htmlhead($checkapi, $rig)
 {
 {
+ global $miner_font_family, $miner_font_size;
  global $error, $readonly, $here;
  global $error, $readonly, $here;
+ global $ignorerefresh, $autorefresh;
+
+ $paramrig = '';
+ if ($rig != null && $rig != '')
+	$paramrig = "&rig=$rig";
+
+ if ($ignorerefresh == true || $autorefresh == 0)
+	$refreshmeta = '';
+ else
+ {
+	$url = "$here?ref=$autorefresh$paramrig";
+	$refreshmeta = "\n<meta http-equiv='refresh' content='$autorefresh;url=$url'>";
+ }
+
  if ($readonly === false && $checkapi === true)
  if ($readonly === false && $checkapi === true)
  {
  {
 	$access = api('privileged');
 	$access = api('privileged');
@@ -87,28 +116,31 @@ function htmlhead($checkapi)
 	||  $access['STATUS']['STATUS'] != 'S')
 	||  $access['STATUS']['STATUS'] != 'S')
 		$readonly = true;
 		$readonly = true;
  }
  }
-?>
-<html><head><title>Mine</title>
+ $miner_font = "font-family:$miner_font_family; font-size:$miner_font_size;";
+
+ echo "<html><head>$refreshmeta
+<title>Mine</title>
 <style type='text/css'>
 <style type='text/css'>
-td { color:blue; font-family:verdana,arial,sans; font-size:13pt; }
-td.h { color:blue; font-family:verdana,arial,sans; font-size:13pt; background:#d0ffff }
-td.err { color:black; font-family:verdana,arial,sans; font-size:13pt; background:#ff3050 }
-td.warn { color:black; font-family:verdana,arial,sans; font-size:13pt; background:#ffb050 }
-td.sta { color:green; font-family:verdana,arial,sans; font-size:13pt; }
-td.tot { color:blue; font-family:verdana,arial,sans; font-size:13pt; background:#fff8f2 }
-td.lst { color:blue; font-family:verdana,arial,sans; font-size:13pt; background:#ffffdd }
+td { color:blue; $miner_font }
+td.h { color:blue; $miner_font background:#d0ffff }
+td.err { color:black; $miner_font background:#ff3050 }
+td.warn { color:black; $miner_font background:#ffb050 }
+td.sta { color:green; $miner_font }
+td.tot { color:blue; $miner_font background:#fff8f2 }
+td.lst { color:blue; $miner_font background:#ffffdd }
 </style>
 </style>
 </head><body bgcolor=#ecffff>
 </head><body bgcolor=#ecffff>
 <script type='text/javascript'>
 <script type='text/javascript'>
-function pr(a,m){if(m!=null){if(!confirm(m+'?'))return}window.location="<?php echo $here ?>"+a}
-<?php
+function pr(a,m){if(m!=null){if(!confirm(m+'?'))return}window.location='$here?ref=$autorefresh'+a}\n";
+
+if ($ignorerefresh == false)
+ echo "function prr(a){if(a){v=document.getElementById('refval').value}else{v=0}window.location='$here?ref='+v+'$paramrig'}\n";
+
  if ($readonly === false && $checkapi === true)
  if ($readonly === false && $checkapi === true)
  {
  {
-?>
-function prc(a,m){pr('?arg='+a,m)}
+echo "function prc(a,m){pr('&arg='+a,m)}
 function prs(a,r){var c=a.substr(3);var z=c.split('|',2);var m=z[0].substr(0,1).toUpperCase()+z[0].substr(1)+' GPU '+z[1];prc(a+'&rig='+r,m)}
 function prs(a,r){var c=a.substr(3);var z=c.split('|',2);var m=z[0].substr(0,1).toUpperCase()+z[0].substr(1)+' GPU '+z[1];prc(a+'&rig='+r,m)}
-function prs2(a,n,r){var v=document.getElementById('gi'+n).value;var c=a.substr(3);var z=c.split('|',2);var m='Set GPU '+z[1]+' '+z[0].substr(0,1).toUpperCase()+z[0].substr(1)+' to '+v;prc(a+','+v+'&rig='+r,m)}
-<?php
+function prs2(a,n,r){var v=document.getElementById('gi'+n).value;var c=a.substr(3);var z=c.split('|',2);var m='Set GPU '+z[1]+' '+z[0].substr(0,1).toUpperCase()+z[0].substr(1)+' to '+v;prc(a+','+v+'&rig='+r,m)}\n";
  }
  }
 ?>
 ?>
 </script>
 </script>
@@ -778,7 +810,7 @@ function doforeach($cmd, $des, $sum, $head, $datetime)
 			foreach ($dthead as $name => $x)
 			foreach ($dthead as $name => $x)
 			{
 			{
 				if ($item == 'STATUS' && $name == '')
 				if ($item == 'STATUS' && $name == '')
-					echo "<td align=right><input type=button value='Rig $rig' onclick='pr(\"?rig=$rig\",null)'></td>";
+					echo "<td align=right><input type=button value='Rig $rig' onclick='pr(\"&rig=$rig\",null)'></td>";
 				else
 				else
 				{
 				{
 					if (isset($row[$name]))
 					if (isset($row[$name]))
@@ -861,7 +893,7 @@ function doforeach($cmd, $des, $sum, $head, $datetime)
 				if ($rig === 'total')
 				if ($rig === 'total')
 					echo "<td align=right class=tot>Total:</td>";
 					echo "<td align=right class=tot>Total:</td>";
 				else
 				else
-					echo "<td align=right><input type=button value='Rig $rig' onclick='pr(\"?rig=$rig\",null)'></td>";
+					echo "<td align=right><input type=button value='Rig $rig' onclick='pr(\"&rig=$rig\",null)'></td>";
 			}
 			}
 			else
 			else
 			{
 			{
@@ -884,27 +916,42 @@ function doforeach($cmd, $des, $sum, $head, $datetime)
  }
  }
 }
 }
 #
 #
+function refreshbuttons()
+{
+ global $readonly;
+ global $ignorerefresh, $changerefresh, $autorefresh;
+
+ if ($ignorerefresh == false && $changerefresh == true)
+ {
+	echo '&nbsp;&nbsp;&nbsp;&nbsp;';
+	echo "<input type=button value='Refresh:' onclick='prr(true)'>";
+	echo "<input type=text name='refval' id='refval' size=2 value='$autorefresh'>";
+	echo "<input type=button value='Off' onclick='prr(false)'>";
+ }
+}
+#
 function doOne($rig, $preprocess)
 function doOne($rig, $preprocess)
 {
 {
- global $error, $readonly, $notify;
- global $rigs;
+ global $error, $readonly, $notify, $rigs;
 
 
- htmlhead(true);
+ htmlhead(true, $rig);
 
 
  $error = null;
  $error = null;
 
 
  echo "<tr><td><table cellpadding=0 cellspacing=0 border=0><tr><td>";
  echo "<tr><td><table cellpadding=0 cellspacing=0 border=0><tr><td>";
- echo "<input type=button value='Refresh' onclick='pr(\"?rig=$rig\",null)'></td>";
+ echo "<input type=button value='Refresh' onclick='pr(\"&rig=$rig\",null)'></td>";
  if (count($rigs) > 1)
  if (count($rigs) > 1)
 	echo "<td><input type=button value='Summary' onclick='pr(\"\",null)'></td>";
 	echo "<td><input type=button value='Summary' onclick='pr(\"\",null)'></td>";
- echo "<td width=100%>&nbsp;</td><td>";
+ echo "<td width=100%>&nbsp;</td><td nowrap>";
  if ($readonly === false)
  if ($readonly === false)
  {
  {
-	$msg = 'Quit CGMiner';
+	$rg = '';
 	if (count($rigs) > 1)
 	if (count($rigs) > 1)
-		$msg .= " Rig $rig";
-	echo "<input type=button value='Quit' onclick='prc(\"quit&rig=$rig\",\"$msg\")'>";
+		$rg = " Rig $rig";
+	echo "<input type=button value='Restart' onclick='prc(\"restart&rig=$rig\",\"Restart CGMiner$rg\")'>";
+	echo "&nbsp;<input type=button value='Quit' onclick='prc(\"quit&rig=$rig\",\"Quit CGMiner$rg\")'>";
  }
  }
+ refreshbuttons();
  echo "</td></tr></table></td></tr>";
  echo "</td></tr></table></td></tr>";
 
 
  if ($preprocess != null)
  if ($preprocess != null)
@@ -930,6 +977,14 @@ function display()
  global $tablebegin, $tableend;
  global $tablebegin, $tableend;
  global $miner, $port;
  global $miner, $port;
  global $error, $readonly, $notify, $rigs;
  global $error, $readonly, $notify, $rigs;
+ global $ignorerefresh, $autorefresh;
+
+ if ($ignorerefresh == false)
+ {
+	$ref = trim(getparam('ref', true));
+	if ($ref != null && $ref != '')
+		$autorefresh = intval($ref);
+ }
 
 
  $rig = trim(getparam('rig', true));
  $rig = trim(getparam('rig', true));
 
 
@@ -998,10 +1053,12 @@ function display()
 	return;
 	return;
  }
  }
 
 
- htmlhead(false);
+ htmlhead(false, null);
 
 
  echo "<tr><td><table cellpadding=0 cellspacing=0 border=0><tr><td>";
  echo "<tr><td><table cellpadding=0 cellspacing=0 border=0><tr><td>";
  echo "<input type=button value='Refresh' onclick='pr(\"\",null)'>";
  echo "<input type=button value='Refresh' onclick='pr(\"\",null)'>";
+ echo "<td width=100%>&nbsp;</td><td nowrap>";
+ refreshbuttons();
  echo "</td></tr></table></td></tr>";
  echo "</td></tr></table></td></tr>";
 
 
  if ($preprocess != null)
  if ($preprocess != null)

+ 11 - 6
util.c

@@ -56,7 +56,7 @@ struct upload_buffer {
 
 
 struct header_info {
 struct header_info {
 	char		*lp_path;
 	char		*lp_path;
-	bool		has_rolltime;
+	int		rolltime;
 	char		*reason;
 	char		*reason;
 };
 };
 
 
@@ -160,8 +160,13 @@ static size_t resp_hdr_cb(void *ptr, size_t size, size_t nmemb, void *user_data)
 		if (!strncasecmp("N", val, 1)) {
 		if (!strncasecmp("N", val, 1)) {
 			applog(LOG_DEBUG, "X-Roll-Ntime: N found");
 			applog(LOG_DEBUG, "X-Roll-Ntime: N found");
 		} else {
 		} else {
-			applog(LOG_DEBUG, "X-Roll-Ntime found");
-			hi->has_rolltime = true;
+			/* Check to see if expire= is supported and if not, set
+			 * the rolltime to the default scantime */
+			if (strlen(val) > 7 && !strncasecmp("expire=", val, 7))
+				sscanf(val + 7, "%d", &hi->rolltime);
+			else
+				hi->rolltime = opt_scantime;
+			applog(LOG_DEBUG, "X-Roll-Ntime expiry set to %d", hi->rolltime);
 		}
 		}
 	}
 	}
 
 
@@ -248,7 +253,7 @@ static void set_nettime(void)
 
 
 json_t *json_rpc_call(CURL *curl, const char *url,
 json_t *json_rpc_call(CURL *curl, const char *url,
 		      const char *userpass, const char *rpc_req,
 		      const char *userpass, const char *rpc_req,
-		      bool probe, bool longpoll, bool *rolltime,
+		      bool probe, bool longpoll, int *rolltime,
 		      struct pool *pool, bool share)
 		      struct pool *pool, bool share)
 {
 {
 	json_t *val, *err_val, *res_val;
 	json_t *val, *err_val, *res_val;
@@ -260,7 +265,7 @@ json_t *json_rpc_call(CURL *curl, const char *url,
 	char len_hdr[64], user_agent_hdr[128];
 	char len_hdr[64], user_agent_hdr[128];
 	char curl_err_str[CURL_ERROR_SIZE];
 	char curl_err_str[CURL_ERROR_SIZE];
 	long timeout = longpoll ? (60 * 60) : 60;
 	long timeout = longpoll ? (60 * 60) : 60;
-	struct header_info hi = {NULL, false, NULL};
+	struct header_info hi = {NULL, 0, NULL};
 	bool probing = false;
 	bool probing = false;
 
 
 	memset(&err, 0, sizeof(err));
 	memset(&err, 0, sizeof(err));
@@ -375,7 +380,7 @@ json_t *json_rpc_call(CURL *curl, const char *url,
 		hi.lp_path = NULL;
 		hi.lp_path = NULL;
 	}
 	}
 
 
-	*rolltime = hi.has_rolltime;
+	*rolltime = hi.rolltime;
 
 
 	val = JSON_LOADS(all_data.buf, &err);
 	val = JSON_LOADS(all_data.buf, &err);
 	if (!val) {
 	if (!val) {