Browse Source

Create a separate thread for handling all work and idle submission to the avalon which messages the scanhash function it has completed to update statistics.

Con Kolivas 12 years ago
parent
commit
629feb5f3f
2 changed files with 128 additions and 144 deletions
  1. 125 144
      driver-avalon.c
  2. 3 0
      driver-avalon.h

+ 125 - 144
driver-avalon.c

@@ -295,47 +295,6 @@ static void avalon_clear_readbuf(int fd)
 	} while (ret > 0);
 }
 
-/* Wait until the avalon says it's ready to receive a write, or 2 seconds has
- * elapsed, whichever comes first. The status is updated by the ftdi device
- * every 40ms. Returns true if the avalon is ready. */
-static bool avalon_wait_write(int fd)
-{
-	int i = 0;
-	bool ret;
-
-	do {
-		ret = avalon_buffer_full(fd);
-		if (ret)
-			nmsleep(50);
-	} while (ret == true && i++ < 40);
-
-	return !ret;
-}
-
-static void avalon_idle(struct cgpu_info *avalon, int fd)
-{
-	struct avalon_info *info = avalon->device_data;
-	int i;
-
-	for (i = 0; i < info->miner_count; i++) {
-		struct avalon_task at;
-		int ret;
-
-		avalon_clear_readbuf(fd);
-		if (unlikely(avalon_buffer_full(fd))) {
-			applog(LOG_WARNING, "Avalon buffer full in avalon_idle after %d tasks", i);
-			break;
-		}
-		avalon_init_task(&at, 0, 0, info->fan_pwm,
-				 info->timeout, info->asic_count,
-				 info->miner_count, 1, 1, info->frequency);
-		ret = avalon_write(fd, (char *)&at, AVALON_WRITE_SIZE);
-		if (unlikely(ret == AVA_SEND_ERROR))
-			break;
-	}
-	applog(LOG_ERR, "Avalon: Going to idle mode");
-}
-
 static int avalon_reset(struct cgpu_info *avalon, int fd)
 {
 	struct avalon_result ar;
@@ -344,20 +303,10 @@ static int avalon_reset(struct cgpu_info *avalon, int fd)
 	int ret, i = 0;
 	struct timespec p;
 
-	/* Reset once, then send command to go idle */
-	ret = avalon_write(fd, &reset, 1);
-	if (unlikely(ret == AVA_SEND_ERROR))
-		return -1;
-	/* Ignore first result as it may be corrupt with old work */
+	/* There's usually a one byte response after opening */
 	avalon_clear_readbuf(fd);
 
-	/* What do these sleeps do?? */
-	p.tv_sec = 0;
-	p.tv_nsec = AVALON_RESET_PITCH;
-	nanosleep(&p, NULL);
-	avalon_idle(avalon, fd);
-
-	/* Reset again, then check result */
+	/* Reset once, then send command to go idle */
 	ret = avalon_write(fd, &reset, 1);
 	if (unlikely(ret == AVA_SEND_ERROR))
 		return -1;
@@ -377,16 +326,19 @@ static int avalon_reset(struct cgpu_info *avalon, int fd)
 	}
 
 	if (i != 11) {
-		applog(LOG_ERR, "Avalon: Reset failed! not an Avalon?"
-		       " (%d: %02x %02x %02x %02x)",
+		applog(LOG_ERR, "AVA%d: Reset failed! not an Avalon?"
+		       " (%d: %02x %02x %02x %02x)", avalon->device_id,
 		       i, buf[0], buf[1], buf[2], buf[3]);
 		/* FIXME: return 1; */
 	} else
-		applog(LOG_WARNING, "Avalon: Reset succeeded");
+		applog(LOG_WARNING, "AVA%d: Reset succeeded",
+		       avalon->device_id);
+
+	/* What do these sleeps do?? */
+	p.tv_sec = 0;
+	p.tv_nsec = AVALON_RESET_PITCH;
+	nanosleep(&p, NULL);
 
-	avalon_idle(avalon, fd);
-	if (!avalon_wait_write(fd))
-		applog(LOG_WARNING, "Avalon: Not ready for writes?");
 	return 0;
 }
 
@@ -693,7 +645,7 @@ static void *avalon_get_results(void *userdata)
 
 	pthread_detach(pthread_self());
 
-	snprintf(threadname, 24, "ava_getres/%d", avalon->device_id);
+	snprintf(threadname, 24, "ava_recv/%d", avalon->device_id);
 	RenameThread(threadname);
 
 	while (42) {
@@ -739,6 +691,80 @@ static void *avalon_get_results(void *userdata)
 	return NULL;
 }
 
+static void avalon_rotate_array(struct cgpu_info *avalon)
+{
+	avalon->queued = 0;
+	if (++avalon->work_array >= AVALON_ARRAY_SIZE)
+		avalon->work_array = 0;
+}
+
+static void *avalon_send_tasks(void *userdata)
+{
+	struct cgpu_info *avalon = (struct cgpu_info *)userdata;
+	struct avalon_info *info = avalon->device_data;
+	const int avalon_get_work_count = info->miner_count;
+	int fd = avalon->device_fd;
+	char threadname[24];
+	bool idle = false;
+
+	pthread_detach(pthread_self());
+
+	snprintf(threadname, 24, "ava_send/%d", avalon->device_id);
+	RenameThread(threadname);
+
+	while (42) {
+		int start_count, end_count, i, j, ret;
+		struct avalon_task at;
+		int idled = 0;
+
+		while (avalon_buffer_full(fd)) {
+			nmsleep(40);
+		}
+
+		mutex_lock(&info->qlock);
+		start_count = avalon->work_array * avalon_get_work_count;
+		end_count = start_count + avalon_get_work_count;
+		for (i = start_count, j = 0; i < end_count; i++, j++) {
+			if (unlikely(avalon_buffer_full(fd))) {
+				applog(LOG_WARNING,
+				       "AVA%i: Buffer full before all  work queued",
+					avalon->device_id);
+				break;
+			}
+
+			if (likely(j < avalon->queued)) {
+				idle = false;
+				avalon_init_task(&at, 0, 0, info->fan_pwm,
+						info->timeout, info->asic_count,
+						info->miner_count, 1, 0, info->frequency);
+				avalon_create_task(&at, avalon->works[i]);
+			} else {
+				idled++;
+				avalon_init_task(&at, 0, 0, info->fan_pwm,
+						info->timeout, info->asic_count,
+						info->miner_count, 1, 1, info->frequency);
+			}
+			ret = avalon_send_task(fd, &at, avalon);
+			if (unlikely(ret == AVA_SEND_ERROR)) {
+				applog(LOG_ERR, "AVA%i: Comms error(buffer)",
+				       avalon->device_id);
+				dev_error(avalon, REASON_DEV_COMMS_ERROR);
+				avalon_reset(avalon, fd);
+			}
+		}
+		pthread_cond_signal(&info->qcond);
+		mutex_unlock(&info->qlock);
+
+		if (unlikely(idled && !idle)) {
+			idle = true;
+			applog(LOG_WARNING, "AVA%i: Idled %d miners",
+			       avalon->device_id, idled);
+		}
+		avalon_rotate_array(avalon);
+	}
+	return NULL;
+}
+
 static bool avalon_prepare(struct thr_info *thr)
 {
 	struct cgpu_info *avalon = thr->cgpu;
@@ -753,8 +779,15 @@ static bool avalon_prepare(struct thr_info *thr)
 
 	info->thr = thr;
 	mutex_init(&info->lock);
+	mutex_init(&info->qlock);
+	if (unlikely(pthread_cond_init(&info->qcond, NULL)))
+		quit(1, "Failed to pthread_cond_init avalon qcond");
+
 	avalon_clear_readbuf(avalon->device_fd);
 
+	if (pthread_create(&info->write_thr, NULL, avalon_send_tasks, (void *)avalon))
+		quit(1, "Failed to create avalon write_thr");
+
 	if (pthread_create(&info->read_thr, NULL, avalon_get_results, (void *)avalon))
 		quit(1, "Failed to create avalon read_thr");
 
@@ -875,112 +908,60 @@ static void avalon_update_temps(struct cgpu_info *avalon, struct avalon_info *in
 static bool avalon_fill(struct cgpu_info *avalon)
 {
 	int subid, slot, mc = avalon_infos[avalon->device_id]->miner_count;
+	struct avalon_info *info = avalon->device_data;
 	struct work *work;
+	bool ret = true;
 
+	mutex_lock(&info->qlock);
 	if (avalon->queued >= mc)
-		return true;
+		goto out_unlock;
 	work = get_queued(avalon);
-	if (unlikely(!work))
-		return false;
+	if (unlikely(!work)) {
+		ret = false;
+		goto out_unlock;
+	}
 	subid = avalon->queued++;
 	work->subid = subid;
 	slot = avalon->work_array * mc + subid;
 	if (likely(avalon->works[slot]))
 		work_completed(avalon, avalon->works[slot]);
 	avalon->works[slot] = work;
-	if (avalon->queued >= mc)
-		return true;
-	return false;
-}
+	if (avalon->queued < mc)
+		ret = false;
+out_unlock:
+	mutex_unlock(&info->qlock);
 
-static void avalon_rotate_array(struct cgpu_info *avalon)
-{
-	avalon->queued = 0;
-	if (++avalon->work_array >= AVALON_ARRAY_SIZE)
-		avalon->work_array = 0;
+	return ret;
 }
 
 static int64_t avalon_scanhash(struct thr_info *thr)
 {
-	struct cgpu_info *avalon;
-	struct work **works;
-	int fd, ret = AVA_GETS_OK, full;
-
-	struct avalon_info *info;
-	struct avalon_task at;
-	int i;
-	int avalon_get_work_count;
-	int start_count, end_count;
-
-	struct timeval tv_start, elapsed;
-	int64_t hash_count;
-	static int first_try = 0;
-
-	avalon = thr->cgpu;
-	works = avalon->works;
-	info = avalon->device_data;
-	avalon_get_work_count = info->miner_count;
-
-	fd = avalon->device_fd;
-#ifndef WIN32
-	tcflush(fd, TCOFLUSH);
-#endif
-
-	start_count = avalon->work_array * avalon_get_work_count;
-	end_count = start_count + avalon_get_work_count;
-	i = start_count;
-	while (true) {
-		avalon_init_task(&at, 0, 0, info->fan_pwm,
-				 info->timeout, info->asic_count,
-				 info->miner_count, 1, 0, info->frequency);
-		avalon_create_task(&at, works[i]);
-		ret = avalon_send_task(fd, &at, avalon);
-		if (unlikely(ret == AVA_SEND_ERROR ||
-			     (ret == AVA_SEND_BUFFER_EMPTY &&
-			      (i + 1 == end_count) &&
-			      first_try))) {
-			applog(LOG_ERR, "AVA%i: Comms error(buffer)",
-			       avalon->device_id);
-			dev_error(avalon, REASON_DEV_COMMS_ERROR);
-			avalon_reset(avalon, fd);
-			first_try = 0;
-			return 0;	/* This should never happen */
-		}
-		if (ret == AVA_SEND_BUFFER_EMPTY && (i + 1 == end_count)) {
-			first_try = 1;
-			avalon_rotate_array(avalon);
-			return 0xffffffff;
-		}
-
-		works[i]->blk.nonce = 0xffffffff;
-
-		if (ret == AVA_SEND_BUFFER_FULL)
-			break;
-
-		i++;
-	}
-	if (unlikely(first_try))
-		first_try = 0;
-
-	elapsed.tv_sec = elapsed.tv_usec = 0;
-	cgtime(&tv_start);
+	struct cgpu_info *avalon = thr->cgpu;
+	struct avalon_info *info = avalon->device_data;
+	struct timeval now, then, tdiff;
+	int64_t hash_count, us_timeout;
+	struct timespec abstime;
+
+	/* Full nonce range */
+	us_timeout = 0x100000000ll / info->asic_count / info->frequency;
+	tdiff.tv_sec = us_timeout / 1000000;
+	tdiff.tv_usec = us_timeout - (tdiff.tv_sec * 1000000);
+	cgtime(&now);
+	timeradd(&now, &tdiff, &then);
+	abstime.tv_sec = then.tv_sec;
+	abstime.tv_nsec = then.tv_usec * 1000;
 
-	while (true) {
-		full = avalon_buffer_full(fd);
-		applog(LOG_DEBUG, "Avalon: Buffer full: %s",
-		       ((full == AVA_BUFFER_FULL) ? "Yes" : "No"));
-		if (unlikely(full == AVA_BUFFER_EMPTY))
-			break;
-		nmsleep(40);
-	}
+	/* Wait until avalon_send_tasks signals us that it has completed
+	 * sending its work or a full nonce range timeout has occurred */
+	mutex_lock(&info->qlock);
+	pthread_cond_timedwait(&info->qcond, &info->qlock, &abstime);
+	mutex_unlock(&info->qlock);
 
 	mutex_lock(&info->lock);
 	hash_count = 0xffffffffull * (uint64_t)info->nonces;
 	info->nonces = 0;
 	mutex_unlock(&info->lock);
 
-	avalon_rotate_array(avalon);
-
 	/* This hashmeter is just a utility counter based on returned shares */
 	return hash_count;
 }

+ 3 - 0
driver-avalon.h

@@ -102,7 +102,10 @@ struct avalon_info {
 
 	struct thr_info *thr;
 	pthread_t read_thr;
+	pthread_t write_thr;
 	pthread_mutex_t lock;
+	pthread_mutex_t qlock;
+	pthread_cond_t qcond;
 	int nonces;
 };