Browse Source

Implement the ability to live add, enable, disable, and switch to pools.

Con Kolivas 14 years ago
parent
commit
b0a8f279f7
3 changed files with 200 additions and 87 deletions
  1. 193 82
      main.c
  2. 1 0
      miner.h
  3. 6 5
      util.c

+ 193 - 82
main.c

@@ -190,9 +190,10 @@ static unsigned int new_blocks;
 static unsigned int local_work;
 static unsigned int total_lo, total_ro;
 
-static struct pool *pools = NULL;
-static struct pool *currentpool;
-static int pool_no;
+#define MAX_POOLS (32)
+
+static struct pool *pools[MAX_POOLS];
+static struct pool *currentpool = NULL;
 static int total_pools;
 static enum pool_strategy pool_strategy = POOL_FAILOVER;
 static int opt_rotate_period;
@@ -222,18 +223,15 @@ static void applog_and_exit(const char *fmt, ...)
 
 static void add_pool(void)
 {
-	int poolno;
 	struct pool *pool;
 
-	poolno = total_pools++;
-	pools = realloc(pools, sizeof(struct pool) * total_pools);
-	if (!pools) {
-		applog(LOG_ERR, "Failed to malloc pools in add_pool");
+	pool = calloc(sizeof(struct pool), 1);
+	if (!pool) {
+		applog(LOG_ERR, "Failed to malloc pool in add_pool");
 		exit (1);
 	}
-	pool = &pools[poolno];
-	memset(pool, 0, sizeof(struct pool));
-	pool->pool_no = poolno;
+	pool->pool_no = total_pools;
+	pools[total_pools++] = pool;
 	if (unlikely(pthread_mutex_init(&pool->pool_lock, NULL))) {
 		applog(LOG_ERR, "Failed to pthread_mutex_init in add_pool");
 		exit (1);
@@ -367,7 +365,7 @@ static char *set_url(const char *arg, char **p)
 	total_urls++;
 	if (total_urls > total_pools)
 		add_pool();
-	pool = &pools[total_urls - 1];
+	pool = pools[total_urls - 1];
 
 	opt_set_charp(arg, &pool->rpc_url);
 	if (strncmp(arg, "http://", 7) &&
@@ -387,7 +385,7 @@ static char *set_user(const char *arg, char **p)
 	if (total_users > total_pools)
 		add_pool();
 
-	pool = &pools[total_users - 1];
+	pool = pools[total_users - 1];
 	opt_set_charp(arg, &pool->rpc_user);
 
 	return NULL;
@@ -403,7 +401,7 @@ static char *set_pass(const char *arg, char **p)
 	if (total_passes > total_pools)
 		add_pool();
 
-	pool = &pools[total_passes - 1];
+	pool = pools[total_passes - 1];
 	opt_set_charp(arg, &pool->rpc_pass);
 
 	return NULL;
@@ -419,7 +417,7 @@ static char *set_userpass(const char *arg, char **p)
 	if (total_userpasses > total_pools)
 		add_pool();
 
-	pool = &pools[total_userpasses - 1];
+	pool = pools[total_userpasses - 1];
 	opt_set_charp(arg, &pool->rpc_userpass);
 
 	return NULL;
@@ -788,11 +786,13 @@ static void print_status(int thr_id)
 
 void log_curses(const char *f, va_list ap)
 {
-	if (curses_active && !opt_loginput) {
-		pthread_mutex_lock(&curses_lock);
-		vw_printw(logwin, f, ap);
-		wrefresh(logwin);
-		pthread_mutex_unlock(&curses_lock);
+	if (curses_active) {
+		if (!opt_loginput) {
+			pthread_mutex_lock(&curses_lock);
+			vw_printw(logwin, f, ap);
+			wrefresh(logwin);
+			pthread_mutex_unlock(&curses_lock);
+		}
 	} else
 		vprintf(f, ap);
 }
@@ -917,9 +917,9 @@ static inline struct pool *select_pool(void)
 		rotating_pool++;
 		if (rotating_pool >= total_pools)
 			rotating_pool = 0;
-		pool = &pools[rotating_pool];
+		pool = pools[rotating_pool];
 		if (!pool->idle && pool->enabled)
-			return &pools[rotating_pool];
+			return pools[rotating_pool];
 	}
 	return current_pool();
 }
@@ -1202,32 +1202,57 @@ static int real_staged(void)
 	return ret;
 }
 
-static void switch_pools(void)
+/* Find the pool that currently has the highest priority */
+static struct pool *priority_pool(int choice)
 {
-	struct pool *pool, *last_pool;
-	int i, pools_active = 0;
+	struct pool *ret = NULL;
+	int i;
 
 	for (i = 0; i < total_pools; i++) {
-		pool = &pools[i];
+		struct pool *pool = pools[i];
 
-		if (!pool->idle && pool->enabled)
-			pools_active++;
+		if (pool->prio == choice) {
+			ret = pool;
+			break;
+		}
 	}
-	
-	if (!pools_active) {
-		applog(LOG_ERR, "No pools active, waiting...");
-		goto out;
+
+	if (unlikely(!ret)) {
+		applog(LOG_ERR, "WTF No pool %d found!", choice);
+		return pools[choice];
 	}
+	return ret;
+}
+
+static void switch_pools(struct pool *selected)
+{
+	struct pool *pool, *last_pool;
+	int i, pool_no;
 
 	pthread_mutex_lock(&control_lock);
 	last_pool = currentpool;
+	pool_no = currentpool->pool_no;
+
+	/* Switch selected to pool number 0 and move the rest down */
+	if (selected) {
+		if (selected->prio != 0) {
+			for (i = 0; i < total_pools; i++) {
+				pool = pools[i];
+				if (pool->prio < selected->prio)
+					pool->prio++;
+			}
+			selected->prio = 0;
+		}
+	}
+
 	switch (pool_strategy) {
 		/* Both of these set to the master pool */
 		case POOL_FAILOVER:
 		case POOL_LOADBALANCE:
 			for (i = 0; i < total_pools; i++) {
-				if (!pools[i].idle && pools[i].enabled) {
-					pool_no = i;
+				pool = priority_pool(i);
+				if (!pool->idle && pool->enabled) {
+					pool_no = pool->pool_no;
 					break;
 				}
 			}
@@ -1235,6 +1260,10 @@ static void switch_pools(void)
 		/* Both of these simply increment and cycle */
 		case POOL_ROUNDROBIN:
 		case POOL_ROTATE:
+			if (selected) {
+				pool_no = selected->pool_no;
+				break;
+			}
 			pool_no++;
 			if (pool_no >= total_pools)
 				pool_no = 0;
@@ -1242,7 +1271,8 @@ static void switch_pools(void)
 		default:
 			break;
 	}
-	currentpool = &pools[pool_no];
+
+	currentpool = pools[pool_no];
 	pool = currentpool;
 	pthread_mutex_unlock(&control_lock);
 
@@ -1253,7 +1283,7 @@ static void switch_pools(void)
 	pthread_mutex_lock(&qd_lock);
 	total_queued = 0;
 	pthread_mutex_unlock(&qd_lock);
-out:
+
 	inc_staged(pool, 1, true);
 }
 
@@ -1330,27 +1360,95 @@ static void *stage_thread(void *userdata)
 	return NULL;
 }
 
+static char *curses_input(const char *query);
+
+static int curses_int(const char *query)
+{
+	int ret;
+	char *cvar;
+
+	cvar = curses_input(query);
+	ret = atoi(cvar);
+	free(cvar);
+	return ret;
+}
+
+static bool input_pool(bool live);
+
 static void display_pools(void)
 {
-	int i, cp = current_pool()->pool_no;
+	int i, active = 0;
+	struct pool *pool;
+	int selected;
+	char input;
 
 	opt_loginput = true;
+updated:
 	clear_logwin();
 	pthread_mutex_lock(&curses_lock);
 	for (i = 0; i < total_pools; i++) {
-		struct pool *pool = &pools[i];
+		pool = pools[i];
 
-		if (i == cp)
+		if (pool == current_pool())
 			wattron(logwin, A_BOLD);
+		if (!pool->enabled)
+			wattron(logwin, A_DIM);
 		wprintw(logwin, "%s Pool %d: %s  User:%s\n", pool->enabled? "Enabled" : "Disabled",
 			pool->pool_no, pool->rpc_url, pool->rpc_user);
-		wattroff(logwin, A_BOLD);
+		wattroff(logwin, A_BOLD | A_DIM);
 	}
-	//wprintw(logwin, "[A]dd pool [S]witch pool [D]isable pool [E]nable pool");
-	wprintw(logwin, "Press any key to continue\n");
+retry:
+	wprintw(logwin, "\n[A]dd pool [S]witch pool [D]isable pool [E]nable pool\n");
+	wprintw(logwin, "Or press any other key to continue\n");
 	wrefresh(logwin);
 	pthread_mutex_unlock(&curses_lock);
-	i = getch();
+	input = getch();
+
+	if (!strncasecmp(&input, "a", 1)) {
+		input_pool(true);
+		goto updated;
+	} else if (!strncasecmp(&input, "s", 1)) {
+		selected = curses_int("Select pool number");
+		if (selected < 0 || selected >= total_pools) {
+			wprintw(logwin, "Invalid selection");
+			goto retry;
+		}
+		pool = pools[selected];
+		pool->enabled = true;
+		switch_pools(pool);
+		goto updated;
+	} else if (!strncasecmp(&input, "d", 1)) {
+		for (i = 0; i < total_pools; i++) {
+			if ((pools[i])->enabled)
+				active++;
+		}
+		if (active <= 1) {
+			wprintw(logwin, "Cannot disable last pool");
+			goto retry;
+		}
+		selected = curses_int("Select pool number");
+		if (selected < 0 || selected >= total_pools) {
+			wprintw(logwin, "Invalid selection");
+			goto retry;
+		}
+		pool = pools[selected];
+		pool->enabled = false;
+		if (pool == current_pool())
+			switch_pools(NULL);
+		goto updated;
+	} else if (!strncasecmp(&input, "e", 1)) {
+		selected = curses_int("Select pool number");
+		if (selected < 0 || selected >= total_pools) {
+			wprintw(logwin, "Invalid selection");
+			goto retry;
+		}
+		pool = pools[selected];
+		pool->enabled = true;
+		if (pool->prio < current_pool()->prio)
+			switch_pools(pool);
+		goto updated;
+	}
+
 	opt_loginput = false;
 	clear_logwin();
 }
@@ -1552,6 +1650,7 @@ static bool pool_active(struct pool *pool)
 		return false;
 	}
 
+	applog(LOG_WARNING, "Testing pool %s", pool->rpc_url);
 	val = json_rpc_call(curl, pool->rpc_url, pool->rpc_userpass, rpc_req,
 			true, false, pool);
 
@@ -1580,9 +1679,11 @@ static bool pool_active(struct pool *pool)
 			free(work);
 		}
 		json_decref(val);
-	} else
+	} else {
 		applog(LOG_DEBUG, "FAILED to retrieve work from pool %u %s",
 		       pool->pool_no, pool->rpc_url);
+		applog(LOG_WARNING, "Pool down, URL or credentials invalid");
+	}
 out:
 	curl_easy_cleanup(curl);
 	return ret;
@@ -1592,14 +1693,14 @@ static void pool_died(struct pool *pool)
 {
 	applog(LOG_WARNING, "Pool %d %s not responding!", pool->pool_no, pool->rpc_url);
 	gettimeofday(&pool->tv_idle, NULL);
-	switch_pools();
+	switch_pools(NULL);
 }
 
 static void pool_resus(struct pool *pool)
 {
 	applog(LOG_WARNING, "Pool %d %s recovered", pool->pool_no, pool->rpc_url);
-	if (pool->pool_no < pool_no && pool_strategy == POOL_FAILOVER)
-		switch_pools();
+	if (pool->prio < current_pool()->prio && pool_strategy == POOL_FAILOVER)
+		switch_pools(NULL);
 }
 
 static bool queue_request(void)
@@ -2518,7 +2619,7 @@ static void *watchdog_thread(void *userdata)
 		gettimeofday(&now, NULL);
 
 		for (i = 0; i < total_pools; i++) {
-			struct pool *pool = &pools[i];
+			struct pool *pool = pools[i];
 
 			if (!pool->enabled)
 				continue;
@@ -2533,7 +2634,7 @@ static void *watchdog_thread(void *userdata)
 
 		if (pool_strategy == POOL_ROTATE && now.tv_sec - rotate_tv.tv_sec > 60 * opt_rotate_period) {
 			gettimeofday(&rotate_tv, NULL);
-			switch_pools();
+			switch_pools(NULL);
 		}
 
 		//for (i = 0; i < mining_threads; i++) {
@@ -2600,7 +2701,7 @@ static void print_summary(void)
 
 	if (total_pools > 1) {
 		for (i = 0; i < total_pools; i++) {
-			struct pool *pool = &pools[i];
+			struct pool *pool = pools[i];
 
 			printf("Pool: %s\n", pool->rpc_url);
 			printf(" Queued work requests: %d\n", pool->getwork_requested);
@@ -2647,6 +2748,7 @@ static char *curses_input(const char *query)
 {
 	char *input;
 
+	echo();
 	input = malloc(255);
 	if (!input)
 		quit(1, "Failed to malloc input");
@@ -2655,44 +2757,43 @@ static char *curses_input(const char *query)
 	wrefresh(logwin);
 	wgetnstr(logwin, input, 255);
 	leaveok(logwin, true);
+	noecho();
 	return input;
 }
 
 static bool input_pool(bool live)
 {
 	char *url, *user, *pass;
-	int poolno = total_pools;
 	struct pool *pool;
+	bool ret = false;
 
-	echo();
 	immedok(logwin, true);
+	if (total_pools == MAX_POOLS) {
+		wprintw(logwin, "Reached maximum number of pools.\n");
+		goto out;
+	}
 	wprintw(logwin, "Input server details.\n");
 
 	url = curses_input("URL");
 	if (strncmp(url, "http://", 7) &&
 	    strncmp(url, "https://", 8)) {
 		applog(LOG_ERR, "URL must start with http:// or https://");
-		return false;
+		goto out;
 	}
 
 	user = curses_input("Username");
 	if (!user)
-		return false;
+		goto out;
 
 	pass = curses_input("Password");
 	if (!pass)
-		return false;
-
-	wclear(logwin);
-	immedok(logwin, false);
-	noecho();
+		goto out;
 
-	pools = realloc(pools, sizeof(struct pool) * (total_pools + 1));
-	if (!pools)
-		quit(1, "Failed to malloc pools in input_pool");
-	pool = &pools[poolno];
-	memset(pool, 0, sizeof(struct pool));
-	pool->pool_no = poolno;
+	pool = calloc(sizeof(struct pool), 1);
+	if (!pool)
+		quit(1, "Failed to realloc pools in input_pool");
+	pool->pool_no = total_pools;
+	pool->prio = total_pools;
 	if (unlikely(pthread_mutex_init(&pool->pool_lock, NULL)))
 		quit (1, "Failed to pthread_mutex_init in input_pool");
 	pool->rpc_url = url;
@@ -2705,18 +2806,25 @@ static bool input_pool(bool live)
 
 	pool->tv_idle.tv_sec = ~0UL;
 
-	if (!live) {
-		total_pools++;
-		return true;
-	}
-
-	/* Test the pool before we enable it if we're live running*/
-	if (pool_active(pool)) {
+	/* Test the pool before we enable it if we're live running, otherwise
+	 * it will be tested separately */
+	ret = true;
+	if (live && pool_active(pool))
 		pool->enabled = true;
-		total_pools++;
-		return true;
+	pools[total_pools++] = pool;
+out:
+	immedok(logwin, false);
+
+	if (!ret) {
+		free(pool);
+		if (url)
+			free(url);
+		if (user)
+			free(user);
+		if (pass)
+			free(pass);
 	}
-	return false;
+	return ret;
 }
 
 int main (int argc, char *argv[])
@@ -2842,7 +2950,7 @@ int main (int argc, char *argv[])
 	}
 
 	for (i = 0; i < total_pools; i++) {
-		struct pool *pool = &pools[i];
+		struct pool *pool = pools[i];
 
 		if (!pool->rpc_userpass) {
 			if (!pool->rpc_user || !pool->rpc_pass)
@@ -2861,8 +2969,8 @@ int main (int argc, char *argv[])
 				quit(1, "Failed to find colon delimiter in userpass");
 		}
 	}
-	/* Set the current pool to pool 0 */
-	currentpool = pools;
+	/* Set the currentpool to pool 0 */
+	currentpool = pools[0];
 
 #ifdef HAVE_SYSLOG_H
 	if (use_syslog)
@@ -2943,13 +3051,18 @@ int main (int argc, char *argv[])
 	for (i = 0; i < total_pools; i++) {
 		struct pool *pool;
 
-		pool = &pools[i];
+		pool = pools[i];
 		if (pool_active(pool)) {
+			if (!currentpool)
+				currentpool = pool;
 			applog(LOG_INFO, "Pool %d %s active", pool->pool_no, pool->rpc_url);
 			pools_active++;
 			pool->enabled = true;
-		} else
+		} else {
+			if (pool == currentpool)
+				currentpool = NULL;
 			applog(LOG_WARNING, "Unable to get work from pool %d %s", pool->pool_no, pool->rpc_url);
+		}
 	}
 
 	if (!pools_active)
@@ -3069,8 +3182,6 @@ int main (int argc, char *argv[])
 		free(gpus);
 	if (opt_n_threads)
 		free(cpus);
-	if (pools)
-		free(pools);
 
 	curl_global_cleanup();
 

+ 1 - 0
miner.h

@@ -267,6 +267,7 @@ typedef struct {
 
 struct pool {
 	int pool_no;
+	int prio;
 	int accepted, rejected;
 	bool submit_fail;
 	bool idle;

+ 6 - 5
util.c

@@ -285,8 +285,11 @@ json_t *json_rpc_call(CURL *curl, const char *url,
 
 	/* it is assumed that 'curl' is freshly [re]initialized at this pt */
 
-	if (probe)
+	if (probe) {
 		probing = ((want_longpoll && !have_longpoll) || !pool->probed);
+		/* Probe for only 10 seconds */
+		curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10);
+	}
 
 	if (opt_protocol)
 		curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
@@ -409,10 +412,8 @@ err_out:
 	databuf_free(&all_data);
 	curl_slist_free_all(headers);
 	curl_easy_reset(curl);
-	if (!successful_connect) {
-		kill_work();
-		applog(LOG_ERR, "Failed to connect - wrong URL or login details?");
-	}
+	if (!successful_connect)
+		applog(LOG_DEBUG, "Failed to connect in json_rpc_call");
 	return NULL;
 }