Browse Source

Use epoll (where available) to immediately interrupt Icarus with new work

This also enables the Icarus to sleep the full 11.2 seconds on read timeouts
Luke Dashjr 13 years ago
parent
commit
8a90e7e694
3 changed files with 68 additions and 14 deletions
  1. 49 13
      driver-icarus.c
  2. 17 1
      miner.c
  3. 2 0
      miner.h

+ 49 - 13
driver-icarus.c

@@ -29,6 +29,8 @@
  *      nonce range is completely calculated.
  */
 
+#include "config.h"
+
 #include <limits.h>
 #include <pthread.h>
 #include <stdio.h>
@@ -264,7 +266,7 @@ static int icarus_open2(const char *devpath, __maybe_unused bool purge)
 
 #define icarus_open(devpath)  icarus_open2(devpath, false)
 
-static int icarus_gets(unsigned char *buf, int fd, struct timeval *tv_finish, bool *wr, int read_count)
+static int icarus_gets(unsigned char *buf, int fd, struct timeval *tv_finish, struct thr_info*thr, int read_count)
 {
 	ssize_t ret = 0;
 	int rc = 0;
@@ -273,8 +275,10 @@ static int icarus_gets(unsigned char *buf, int fd, struct timeval *tv_finish, bo
 	bool first = true;
 
 #ifdef HAVE_EPOLL
-	struct epoll_event ev, evr;
-	epollfd = epoll_create(1);
+	struct epoll_event ev;
+	struct epoll_event evr[2];
+	int epoll_timeout = ICARUS_READ_FAULT_DECISECONDS * 100;
+	epollfd = epoll_create(2);
 	if (epollfd != -1) {
 		ev.events = EPOLLIN;
 		ev.data.fd = fd;
@@ -282,14 +286,37 @@ static int icarus_gets(unsigned char *buf, int fd, struct timeval *tv_finish, bo
 			close(epollfd);
 			epollfd = -1;
 		}
+		if (thr->work_restart_fd != -1)
+		{
+			ev.data.fd = thr->work_restart_fd;
+			if (-1 == epoll_ctl(epollfd, EPOLL_CTL_ADD, thr->work_restart_fd, &ev))
+				applog(LOG_ERR, "Icarus: Error adding work restart fd to epoll");
+			else
+			{
+				epoll_timeout *= read_count;
+				read_count = 1;
+			}
+		}
 	}
+	else
+		applog(LOG_ERR, "Icarus: Error creating epoll");
 #endif
 
 	// Read reply 1 byte at a time to get earliest tv_finish
 	while (true) {
 #ifdef HAVE_EPOLL
-		if (epollfd != -1 && epoll_wait(epollfd, &evr, 1, ICARUS_READ_FAULT_DECISECONDS * 100) != 1)
-			ret = 0;
+		if (epollfd != -1 && (ret = epoll_wait(epollfd, evr, 2, epoll_timeout)) != -1)
+		{
+			if (ret == 1 && evr[0].data.fd == fd)
+				ret = read(fd, buf, 1);
+			else
+			{
+				if (ret)
+					// work restart trigger
+					(void)read(thr->work_restart_fd, buf, read_amount);
+				ret = 0;
+			}
+		}
 		else
 #endif
 		ret = read(fd, buf, 1);
@@ -312,14 +339,14 @@ static int icarus_gets(unsigned char *buf, int fd, struct timeval *tv_finish, bo
 		}
 			
 		rc++;
-		if (rc >= read_count || *wr) {
+		if (rc >= read_count || thr->work_restart) {
 			if (epollfd != -1)
 				close(epollfd);
 			if (opt_debug) {
 				rc *= ICARUS_READ_FAULT_DECISECONDS;
 				applog(LOG_DEBUG,
 			        "Icarus Read: %s %d.%d seconds",
-			        (*wr) ? "Work restart at" : "No data in",
+			        thr->work_restart ? "Work restart at" : "No data in",
 			        rc / 10, rc % 10);
 			}
 			return 1;
@@ -474,9 +501,11 @@ static bool icarus_detect_one(const char *devpath)
 	icarus_write(fd, ob_bin, sizeof(ob_bin));
 
 	memset(nonce_bin, 0, sizeof(nonce_bin));
-	bool wr = 0;
+	struct thr_info dummy = {
+		.work_restart_fd = -1,
+	};
 	struct timeval tv_finish;
-	icarus_gets(nonce_bin, fd, &tv_finish, &wr, 1);
+	icarus_gets(nonce_bin, fd, &tv_finish, &dummy, 1);
 
 	icarus_close(fd);
 
@@ -571,14 +600,21 @@ static bool icarus_prepare(struct thr_info *thr)
 	thr->cgpu_data = state = calloc(1, sizeof(*state));
 	state->firstrun = true;
 
+#ifdef HAVE_EPOLL
+	int epollfd = epoll_create(2);
+	if (epollfd != -1)
+	{
+		close(epollfd);
+		thr->work_restart_fd = 0;
+	}
+#endif
+
 	return true;
 }
 
 static uint64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 				__maybe_unused uint64_t max_nonce)
 {
-	bool *wr = &thr->work_restart;
-
 	struct cgpu_info *icarus;
 	int fd;
 	int ret, lret;
@@ -623,8 +659,8 @@ static uint64_t icarus_scanhash(struct thr_info *thr, struct work *work,
 		{
 			/* Icarus will return 4 bytes (ICARUS_READ_SIZE) nonces or nothing */
 			info = icarus_info[icarus->device_id];
-			lret = icarus_gets(nonce_bin, fd, &tv_finish, wr, info->read_count);
-			if (lret && *wr) {
+			lret = icarus_gets(nonce_bin, fd, &tv_finish, thr, info->read_count);
+			if (lret && thr->work_restart) {
 				// The prepared work is invalid, and the current work is abandoned
 				// Go back to the main loop to get the next work, and stuff
 				// Returning to the main loop will clear work_restart, so use a flag...

+ 17 - 1
miner.c

@@ -2392,7 +2392,7 @@ static bool queue_request(struct thr_info *thr, bool needed);
 
 static void restart_threads(void)
 {
-	int i, j, stale;
+	int i, j, stale, fd;
 	struct cgpu_info *cgpu;
 	struct thr_info *thr;
 
@@ -2408,7 +2408,10 @@ static void restart_threads(void)
 		for (j = 0; j < cgpu->threads; ++j)
 		{
 			thr = &cgpu->thread[j];
+			fd = thr->_work_restart_fd_w;
 			thr->work_restart = true;
+			if (fd != -1)
+				write(fd, "\0", 1);
 		}
 	}
 }
@@ -5218,6 +5221,7 @@ begin_bench:
 			thr->id = k;
 			thr->cgpu = cgpu;
 			thr->device_thread = j;
+			thr->work_restart_fd = thr->_work_restart_fd_w = -1;
 
 			thr->q = tq_new();
 			if (!thr->q)
@@ -5234,6 +5238,18 @@ begin_bench:
 			if (cgpu->api->thread_prepare && !cgpu->api->thread_prepare(thr))
 				continue;
 
+			if (!thr->work_restart_fd)
+			{
+				int pipefd[2];
+				if (!pipe(pipefd))
+				{
+					thr->work_restart_fd = pipefd[0];
+					thr->_work_restart_fd_w = pipefd[1];
+				}
+				else
+					thr->work_restart_fd = -1;
+			}
+
 			thread_reportout(thr);
 
 			if (unlikely(thr_info_create(thr, NULL, miner_thread, thr)))

+ 2 - 0
miner.h

@@ -392,6 +392,8 @@ struct thr_info {
 	double	rolling;
 
 	bool	work_restart;
+	int		work_restart_fd;
+	int		_work_restart_fd_w;
 };
 
 extern int thr_info_create(struct thr_info *thr, pthread_attr_t *attr, void *(*start) (void *), void *arg);