Browse Source

Merge branch 'queuedwork' into bfgminer

Luke Dashjr 13 years ago
parent
commit
85f7147aa7
5 changed files with 578 additions and 87 deletions
  1. 150 39
      deviceapi.c
  2. 2 0
      deviceapi.h
  3. 414 44
      driver-bitforce.c
  4. 1 0
      miner.c
  5. 11 4
      miner.h

+ 150 - 39
deviceapi.c

@@ -122,10 +122,28 @@ int restart_wait(struct thr_info *thr, unsigned int mstime)
 	}
 	}
 }
 }
 
 
+static
+struct work *get_and_prepare_work(struct thr_info *thr)
+{
+	struct cgpu_info *proc = thr->cgpu;
+	const struct device_api *api = proc->api;
+	struct work *work;
+	
+	work = get_work(thr);
+	if (!work)
+		return NULL;
+	if (api->prepare_work && !api->prepare_work(thr, work)) {
+		free_work(work);
+		applog(LOG_ERR, "%"PRIpreprv": Work prepare failed, disabling!", proc->proc_repr);
+		proc->deven = DEV_RECOVER_ERR;
+		return NULL;
+	}
+	return work;
+}
+
 // Miner loop to manage a single processor (with possibly multiple threads per processor)
 // Miner loop to manage a single processor (with possibly multiple threads per processor)
 void minerloop_scanhash(struct thr_info *mythr)
 void minerloop_scanhash(struct thr_info *mythr)
 {
 {
-	const int thr_id = mythr->id;
 	struct cgpu_info *cgpu = mythr->cgpu;
 	struct cgpu_info *cgpu = mythr->cgpu;
 	const struct device_api *api = cgpu->api;
 	const struct device_api *api = cgpu->api;
 	struct timeval tv_start, tv_end;
 	struct timeval tv_start, tv_end;
@@ -138,12 +156,9 @@ void minerloop_scanhash(struct thr_info *mythr)
 	while (1) {
 	while (1) {
 		mythr->work_restart = false;
 		mythr->work_restart = false;
 		request_work(mythr);
 		request_work(mythr);
-		work = get_work(mythr);
-		if (api->prepare_work && !api->prepare_work(mythr, work)) {
-			applog(LOG_ERR, "work prepare failed, exiting "
-				"mining thread %d", thr_id);
+		work = get_and_prepare_work(mythr);
+		if (!work)
 			break;
 			break;
-		}
 		gettimeofday(&(work->tv_work_start), NULL);
 		gettimeofday(&(work->tv_work_start), NULL);
 		
 		
 		do {
 		do {
@@ -199,12 +214,9 @@ bool do_job_prepare(struct thr_info *mythr, struct timeval *tvp_now)
 		// FIXME: Allow get_work to return NULL to retry on notification
 		// FIXME: Allow get_work to return NULL to retry on notification
 		if (mythr->next_work)
 		if (mythr->next_work)
 			free_work(mythr->next_work);
 			free_work(mythr->next_work);
-		mythr->next_work = get_work(mythr);
-		if (api->prepare_work && !api->prepare_work(mythr, mythr->next_work)) {
-			applog(LOG_ERR, "%"PRIpreprv": Work prepare failed, disabling!", proc->proc_repr);
-			proc->deven = DEV_RECOVER_ERR;
+		mythr->next_work = get_and_prepare_work(mythr);
+		if (!mythr->next_work)
 			return false;
 			return false;
-		}
 		mythr->starting_next_work = true;
 		mythr->starting_next_work = true;
 		api->job_prepare(mythr, mythr->next_work, mythr->_max_nonce);
 		api->job_prepare(mythr, mythr->next_work, mythr->_max_nonce);
 	}
 	}
@@ -329,6 +341,44 @@ bool do_process_results(struct thr_info *mythr, struct timeval *tvp_now, struct
 	return true;
 	return true;
 }
 }
 
 
+static
+void do_notifier_select(struct thr_info *thr, struct timeval *tvp_timeout)
+{
+	struct cgpu_info *cgpu = thr->cgpu;
+	struct timeval tv_now;
+	int maxfd;
+	fd_set rfds;
+	
+	gettimeofday(&tv_now, NULL);
+	FD_ZERO(&rfds);
+	FD_SET(thr->notifier[0], &rfds);
+	maxfd = thr->notifier[0];
+	FD_SET(thr->work_restart_notifier[0], &rfds);
+	set_maxfd(&maxfd, thr->work_restart_notifier[0]);
+	if (thr->mutex_request[1] != INVSOCK)
+	{
+		FD_SET(thr->mutex_request[0], &rfds);
+		set_maxfd(&maxfd, thr->mutex_request[0]);
+	}
+	if (select(maxfd + 1, &rfds, NULL, NULL, select_timeout(tvp_timeout, &tv_now)) < 0)
+		return;
+	if (thr->mutex_request[1] != INVSOCK && FD_ISSET(thr->mutex_request[0], &rfds))
+	{
+		// FIXME: This can only handle one request at a time!
+		pthread_mutex_t *mutexp = &cgpu->device_mutex;
+		notifier_read(thr->mutex_request);
+		mutex_lock(mutexp);
+		pthread_cond_signal(&cgpu->device_cond);
+		pthread_cond_wait(&cgpu->device_cond, mutexp);
+		mutex_unlock(mutexp);
+	}
+	if (FD_ISSET(thr->notifier[0], &rfds)) {
+		notifier_read(thr->notifier);
+	}
+	if (FD_ISSET(thr->work_restart_notifier[0], &rfds))
+		notifier_read(thr->work_restart_notifier);
+}
+
 void minerloop_async(struct thr_info *mythr)
 void minerloop_async(struct thr_info *mythr)
 {
 {
 	struct thr_info *thr = mythr;
 	struct thr_info *thr = mythr;
@@ -337,8 +387,6 @@ void minerloop_async(struct thr_info *mythr)
 	struct timeval tv_now;
 	struct timeval tv_now;
 	struct timeval tv_timeout;
 	struct timeval tv_timeout;
 	struct cgpu_info *proc;
 	struct cgpu_info *proc;
-	int maxfd;
-	fd_set rfds;
 	bool is_running, should_be_running;
 	bool is_running, should_be_running;
 	
 	
 	if (mythr->work_restart_notifier[1] == -1)
 	if (mythr->work_restart_notifier[1] == -1)
@@ -397,35 +445,98 @@ defer_events:
 			reduce_timeout_to(&tv_timeout, &mythr->tv_poll);
 			reduce_timeout_to(&tv_timeout, &mythr->tv_poll);
 		}
 		}
 		
 		
-		mythr = thr;
+		do_notifier_select(thr, &tv_timeout);
+	}
+}
+
+static
+void do_queue_flush(struct thr_info *mythr)
+{
+	struct cgpu_info *proc = mythr->cgpu;
+	const struct device_api *api = proc->api;
+	
+	api->queue_flush(mythr);
+	if (mythr->next_work)
+	{
+		free_work(mythr->next_work);
+		mythr->next_work = NULL;
+	}
+}
+
+void minerloop_queue(struct thr_info *thr)
+{
+	struct thr_info *mythr;
+	struct cgpu_info *cgpu = thr->cgpu;
+	const struct device_api *api = cgpu->api;
+	struct timeval tv_now;
+	struct timeval tv_timeout;
+	struct cgpu_info *proc;
+	bool should_be_running;
+	struct work *work;
+	
+	if (thr->work_restart_notifier[1] == -1)
+		notifier_init(thr->work_restart_notifier);
+	
+	while (1) {
+		tv_timeout.tv_sec = -1;
 		gettimeofday(&tv_now, NULL);
 		gettimeofday(&tv_now, NULL);
-		FD_ZERO(&rfds);
-		FD_SET(mythr->notifier[0], &rfds);
-		maxfd = mythr->notifier[0];
-		FD_SET(mythr->work_restart_notifier[0], &rfds);
-		set_maxfd(&maxfd, mythr->work_restart_notifier[0]);
-		if (thr->mutex_request[1] != INVSOCK)
-		{
-			FD_SET(thr->mutex_request[0], &rfds);
-			set_maxfd(&maxfd, thr->mutex_request[0]);
-		}
-		if (select(maxfd + 1, &rfds, NULL, NULL, select_timeout(&tv_timeout, &tv_now)) < 0)
-			continue;
-		if (thr->mutex_request[1] != INVSOCK && FD_ISSET(thr->mutex_request[0], &rfds))
+		for (proc = cgpu; proc; proc = proc->next_proc)
 		{
 		{
-			// FIXME: This can only handle one request at a time!
-			pthread_mutex_t *mutexp = &cgpu->device_mutex;
-			notifier_read(thr->mutex_request);
-			mutex_lock(mutexp);
-			pthread_cond_signal(&cgpu->device_cond);
-			pthread_cond_wait(&cgpu->device_cond, mutexp);
-			mutex_unlock(mutexp);
-		}
-		if (FD_ISSET(mythr->notifier[0], &rfds)) {
-			notifier_read(mythr->notifier);
+			mythr = proc->thr[0];
+			
+			should_be_running = (proc->deven == DEV_ENABLED && !mythr->pause);
+redo:
+			if (should_be_running)
+			{
+				if (unlikely(!mythr->_last_sbr_state))
+				{
+					mt_disable_finish(mythr);
+					mythr->_last_sbr_state = should_be_running;
+				}
+				
+				if (unlikely(mythr->work_restart))
+				{
+					mythr->work_restart = false;
+					do_queue_flush(mythr);
+				}
+				
+				while (!mythr->queue_full)
+				{
+					if (mythr->next_work)
+					{
+						work = mythr->next_work;
+						mythr->next_work = NULL;
+					}
+					else
+					{
+						request_work(mythr);
+						// FIXME: Allow get_work to return NULL to retry on notification
+						work = get_and_prepare_work(mythr);
+					}
+					if (!work)
+						break;
+					if (!api->queue_append(mythr, work))
+						mythr->next_work = work;
+				}
+			}
+			else
+			if (unlikely(mythr->_last_sbr_state))
+			{
+				mythr->_last_sbr_state = should_be_running;
+				do_queue_flush(mythr);
+			}
+			
+			if (timer_passed(&mythr->tv_poll, &tv_now))
+				api->poll(mythr);
+			
+			should_be_running = (proc->deven == DEV_ENABLED && !mythr->pause);
+			if (should_be_running && !mythr->queue_full)
+				goto redo;
+			
+			reduce_timeout_to(&tv_timeout, &mythr->tv_poll);
 		}
 		}
-		if (FD_ISSET(mythr->work_restart_notifier[0], &rfds))
-			notifier_read(mythr->work_restart_notifier);
+		
+		do_notifier_select(thr, &tv_timeout);
 	}
 	}
 }
 }
 
 

+ 2 - 0
deviceapi.h

@@ -28,6 +28,8 @@ extern void job_start_abort(struct thr_info *, bool failure);
 extern bool do_process_results(struct thr_info *, struct timeval *tvp_now, struct work *, bool stopping);
 extern bool do_process_results(struct thr_info *, struct timeval *tvp_now, struct work *, bool stopping);
 extern void minerloop_async(struct thr_info *);
 extern void minerloop_async(struct thr_info *);
 
 
+extern void minerloop_queue(struct thr_info *);
+
 extern void *miner_thread(void *);
 extern void *miner_thread(void *);
 
 
 #endif
 #endif

+ 414 - 44
driver-bitforce.c

@@ -33,20 +33,26 @@
 #define MAX_START_DELAY_MS 100
 #define MAX_START_DELAY_MS 100
 #define tv_to_ms(tval) ((unsigned long)(tval.tv_sec * 1000 + tval.tv_usec / 1000))
 #define tv_to_ms(tval) ((unsigned long)(tval.tv_sec * 1000 + tval.tv_usec / 1000))
 #define TIME_AVG_CONSTANT 8
 #define TIME_AVG_CONSTANT 8
+#define BITFORCE_MAX_QUEUED 10
+#define BITFORCE_MAX_QRESULTS 10
+#define BITFORCE_GOAL_QRESULTS (BITFORCE_MAX_QRESULTS / 2)
 
 
 enum bitforce_proto {
 enum bitforce_proto {
 	BFP_WORK,
 	BFP_WORK,
 	BFP_RANGE,
 	BFP_RANGE,
 	BFP_QUEUE,
 	BFP_QUEUE,
+	BFP_BQUEUE,
 };
 };
 
 
 static const char *protonames[] = {
 static const char *protonames[] = {
 	"full work",
 	"full work",
 	"nonce range",
 	"nonce range",
 	"work queue",
 	"work queue",
+	"bulk queue",
 };
 };
 
 
 struct device_api bitforce_api;
 struct device_api bitforce_api;
+struct device_api bitforce_queue_api;
 
 
 // Code must deal with a timeout
 // Code must deal with a timeout
 #define BFopen(devpath)  serial_open(devpath, 0, 250, true)
 #define BFopen(devpath)  serial_open(devpath, 0, 250, true)
@@ -176,6 +182,8 @@ static bool bitforce_detect_one(const char *devpath)
 	// We have a real BitForce!
 	// We have a real BitForce!
 	bitforce = calloc(1, sizeof(*bitforce));
 	bitforce = calloc(1, sizeof(*bitforce));
 	bitforce->api = &bitforce_api;
 	bitforce->api = &bitforce_api;
+	if (initdata->sc && procs > 1)
+		bitforce->api = &bitforce_queue_api;
 	bitforce->device_path = strdup(devpath);
 	bitforce->device_path = strdup(devpath);
 	bitforce->deven = DEV_ENABLED;
 	bitforce->deven = DEV_ENABLED;
 	bitforce->procs = procs;
 	bitforce->procs = procs;
@@ -208,13 +216,17 @@ struct bitforce_data {
 	unsigned char *next_work_obs;    // Start of data to send
 	unsigned char *next_work_obs;    // Start of data to send
 	unsigned char next_work_obsz;
 	unsigned char next_work_obsz;
 	const char *next_work_cmd;
 	const char *next_work_cmd;
-	char noncebuf[0x200];  // Large enough for 3 works of queue results
+	char noncebuf[14 + (BITFORCE_MAX_QUEUED * 165)];
 	int poll_func;
 	int poll_func;
 	enum bitforce_proto proto;
 	enum bitforce_proto proto;
 	bool sc;
 	bool sc;
-	bool queued;
+	int queued;
+	bool already_have_results;
+	bool just_flushed;
+	int ready_to_queue;
 	unsigned result_busy_polled;
 	unsigned result_busy_polled;
 	unsigned sleep_ms_default;
 	unsigned sleep_ms_default;
+	struct timeval tv_hashmeter_start;
 };
 };
 
 
 static void bitforce_clear_buffer(struct cgpu_info *);
 static void bitforce_clear_buffer(struct cgpu_info *);
@@ -483,6 +495,8 @@ bool bitforce_job_prepare(struct thr_info *thr, struct work *work, __maybe_unuse
 	memcpy(ob_dt, work->data + 64, 12);
 	memcpy(ob_dt, work->data + 64, 12);
 	switch (data->proto)
 	switch (data->proto)
 	{
 	{
+		case BFP_BQUEUE:
+			quit(1, "%"PRIpreprv": Impossible BFP_BQUEUE in bitforce_job_prepare", bitforce->proc_repr);
 		case BFP_RANGE:
 		case BFP_RANGE:
 		{
 		{
 			uint32_t *ob_nonce = (uint32_t*)&(ob_dt[32]);
 			uint32_t *ob_nonce = (uint32_t*)&(ob_dt[32]);
@@ -513,7 +527,7 @@ bool bitforce_job_prepare(struct thr_info *thr, struct work *work, __maybe_unuse
 				else
 				else
 				{
 				{
 					dbg_block_data(bitforce);
 					dbg_block_data(bitforce);
-					data->queued = true;
+					data->queued = 1;
 				}
 				}
 			}
 			}
 			// fallthru...
 			// fallthru...
@@ -595,11 +609,16 @@ void bitforce_job_start(struct thr_info *thr)
 	
 	
 	if (data->queued)
 	if (data->queued)
 	{
 	{
+		uint32_t delay;
+		
 		// get_results collected more accurate job start time
 		// get_results collected more accurate job start time
 		mt_job_transition(thr);
 		mt_job_transition(thr);
 		job_start_complete(thr);
 		job_start_complete(thr);
-		data->queued = false;
-		timer_set_delay(&thr->tv_morework, &bitforce->work_start_tv, bitforce->sleep_ms * 1000);
+		data->queued = 0;
+		delay = (uint32_t)bitforce->sleep_ms * 1000;
+		if (unlikely(data->already_have_results))
+			delay = 0;
+		timer_set_delay(&thr->tv_morework, &bitforce->work_start_tv, delay);
 		return;
 		return;
 	}
 	}
 
 
@@ -654,11 +673,63 @@ commerr:
 static char _discardedbuf[0x10];
 static char _discardedbuf[0x10];
 
 
 static
 static
-void bitforce_job_get_results(struct thr_info *thr, struct work *work)
+int bitforce_zox(struct thr_info *thr, const char *cmd)
 {
 {
 	struct cgpu_info *bitforce = thr->cgpu;
 	struct cgpu_info *bitforce = thr->cgpu;
 	struct bitforce_data *data = bitforce->cgpu_data;
 	struct bitforce_data *data = bitforce->cgpu_data;
 	pthread_mutex_t *mutexp = &bitforce->device->device_mutex;
 	pthread_mutex_t *mutexp = &bitforce->device->device_mutex;
+	int fd = bitforce->device->device_fd;
+	char *pdevbuf = &data->noncebuf[0];
+	int count;
+	
+	mutex_lock(mutexp);
+	bitforce_cmd1(fd, data->xlink_id, pdevbuf, sizeof(data->noncebuf), cmd);
+	if (!strncasecmp(pdevbuf, "INPROCESS:", 10))
+		BFgets(pdevbuf, sizeof(data->noncebuf), fd);
+	if (!strncasecmp(pdevbuf, "COUNT:", 6))
+	{
+		count = atoi(&pdevbuf[6]);
+		size_t cls = strlen(pdevbuf);
+		char *pmorebuf = &pdevbuf[cls];
+		size_t szleft = sizeof(data->noncebuf) - cls, sz;
+		
+		if (count && data->queued)
+			gettimeofday(&bitforce->work_start_tv, NULL);
+		
+		while (true)
+		{
+			BFgets(pmorebuf, szleft, fd);
+			if (!strncasecmp(pmorebuf, "OK", 2))
+			{
+				pmorebuf[0] = '\0';  // process expects only results
+				break;
+			}
+			sz = strlen(pmorebuf);
+			szleft -= sz;
+			pmorebuf += sz;
+			if (unlikely(!szleft))
+			{
+				// Out of buffer space somehow :(
+				applog(LOG_DEBUG, "%"PRIpreprv": Ran out of buffer space for results, discarding extra data", bitforce->proc_repr);
+				pmorebuf = _discardedbuf;
+				szleft = sizeof(_discardedbuf);
+			}
+		}
+	}
+	else
+		count = -1;
+	mutex_unlock(mutexp);
+	
+	return count;
+}
+
+static inline char *next_line(char *);
+
+static
+void bitforce_job_get_results(struct thr_info *thr, struct work *work)
+{
+	struct cgpu_info *bitforce = thr->cgpu;
+	struct bitforce_data *data = bitforce->cgpu_data;
 	int fdDev = bitforce->device->device_fd;
 	int fdDev = bitforce->device->device_fd;
 	unsigned int delay_time_ms;
 	unsigned int delay_time_ms;
 	struct timeval elapsed;
 	struct timeval elapsed;
@@ -692,45 +763,16 @@ void bitforce_job_get_results(struct thr_info *thr, struct work *work)
 	}
 	}
 
 
 	while (1) {
 	while (1) {
-		const char *cmd = (data->proto == BFP_QUEUE) ? "ZOX" : "ZFX";
-		mutex_lock(mutexp);
-		bitforce_cmd1(fdDev, data->xlink_id, pdevbuf, sizeof(data->noncebuf), cmd);
-		if (!strncasecmp(pdevbuf, "COUNT:", 6))
+		if (data->already_have_results)
 		{
 		{
-			count = atoi(&pdevbuf[6]);
-			size_t cls = strlen(pdevbuf);
-			char *pmorebuf = &pdevbuf[cls];
-			size_t szleft = sizeof(data->noncebuf) - cls, sz;
-			
-			if (count && data->queued)
-			{
-				gettimeofday(&now, NULL);
-				bitforce->work_start_tv = now;
-			}
-			
-			while (true)
-			{
-				BFgets(pmorebuf, szleft, fdDev);
-				if (!strncasecmp(pmorebuf, "OK", 2))
-				{
-					pmorebuf[0] = '\0';  // process expects only results
-					break;
-				}
-				sz = strlen(pmorebuf);
-				szleft -= sz;
-				pmorebuf += sz;
-				if (unlikely(!szleft))
-				{
-					// Out of buffer space somehow :(
-					applog(LOG_DEBUG, "%"PRIpreprv": Ran out of buffer space for results, discarding extra data", bitforce->proc_repr);
-					pmorebuf = _discardedbuf;
-					szleft = sizeof(_discardedbuf);
-				}
-			}
+			data->already_have_results = false;
+			strcpy(pdevbuf, "COUNT:0");
+			count = 1;
+			break;
 		}
 		}
-		else
-			count = -1;
-		mutex_unlock(mutexp);
+		
+		const char *cmd = (data->proto == BFP_QUEUE) ? "ZOX" : "ZFX";
+		count = bitforce_zox(thr, cmd);
 
 
 		gettimeofday(&now, NULL);
 		gettimeofday(&now, NULL);
 		timersub(&now, &bitforce->work_start_tv, &elapsed);
 		timersub(&now, &bitforce->work_start_tv, &elapsed);
@@ -741,6 +783,40 @@ void bitforce_job_get_results(struct thr_info *thr, struct work *work)
 			goto out;
 			goto out;
 		}
 		}
 
 
+		if (count > 0)
+		{
+			// Check that queue results match the current work
+			// Also, if there are results from the next work, short-circuit this wait
+			unsigned char midstate[32], datatail[12];
+			char *p;
+			int i;
+			
+			p = pdevbuf;
+			for (i = 0; i < count; ++i)
+			{
+				p = next_line(p);
+				hex2bin(midstate, p, 32);
+				hex2bin(datatail, &p[65], 12);
+				if (!(memcmp(work->midstate, midstate, 32) || memcmp(&work->data[64], datatail, 12)))
+					break;
+			}
+			if (i == count)
+			{
+				// Didn't find the one we're waiting on
+				// Must be extra stuff in the queue results
+				applog(LOG_WARNING, "%"PRIpreprv": Found extra garbage in queue results: %s",
+				       bitforce->proc_repr, pdevbuf);
+				count = 0;
+			}
+			else
+			if (i == count - 1)
+				// Last one found is what we're looking for
+			{}
+			else
+				// We finished the next job too!
+				data->already_have_results = true;
+		}
+		
 		if (!count)
 		if (!count)
 			goto noqr;
 			goto noqr;
 		if (pdevbuf[0] && strncasecmp(pdevbuf, "B", 1)) /* BFL does not respond during throttling */
 		if (pdevbuf[0] && strncasecmp(pdevbuf, "B", 1)) /* BFL does not respond during throttling */
@@ -991,6 +1067,8 @@ static bool bitforce_thread_init(struct thr_info *thr)
 	
 	
 	for ( ; bitforce; bitforce = bitforce->next_proc)
 	for ( ; bitforce; bitforce = bitforce->next_proc)
 	{
 	{
+		thr = bitforce->thr[0];
+		
 		if (unlikely(xlink_id > 30))
 		if (unlikely(xlink_id > 30))
 		{
 		{
 			applog(LOG_ERR, "%"PRIpreprv": Failed to find XLINK address", bitforce->proc_repr);
 			applog(LOG_ERR, "%"PRIpreprv": Failed to find XLINK address", bitforce->proc_repr);
@@ -1014,7 +1092,14 @@ static bool bitforce_thread_init(struct thr_info *thr)
 			data->next_work_ob[8+32+12+8] = '\xAA';
 			data->next_work_ob[8+32+12+8] = '\xAA';
 			data->next_work_obs = &data->next_work_ob[7];
 			data->next_work_obs = &data->next_work_ob[7];
 			
 			
-			bitforce_change_mode(bitforce, BFP_QUEUE);
+			if (bitforce->api == &bitforce_queue_api)
+			{
+				INIT_LIST_HEAD(&thr->work_list);
+				bitforce_change_mode(bitforce, BFP_BQUEUE);
+				timer_set_delay_from_now(&thr->tv_poll, 0);
+			}
+			else
+				bitforce_change_mode(bitforce, BFP_QUEUE);
 		}
 		}
 		else
 		else
 		{
 		{
@@ -1097,3 +1182,288 @@ struct device_api bitforce_api = {
 	.thread_shutdown = bitforce_shutdown,
 	.thread_shutdown = bitforce_shutdown,
 	.thread_enable = biforce_thread_enable
 	.thread_enable = biforce_thread_enable
 };
 };
+
+
+static inline
+void bitforce_set_queue_full(struct thr_info *thr)
+{
+	struct cgpu_info *bitforce = thr->cgpu;
+	struct bitforce_data *data = bitforce->cgpu_data;
+	
+	thr->queue_full = (data->queued + data->ready_to_queue >= BITFORCE_MAX_QUEUED);
+}
+
+static
+bool bitforce_send_queue(struct thr_info *thr)
+{
+	struct cgpu_info *bitforce = thr->cgpu;
+	struct bitforce_data *data = bitforce->cgpu_data;
+	pthread_mutex_t *mutexp = &bitforce->device->device_mutex;
+	int fd = bitforce->device->device_fd;
+	struct work *work;
+	struct list_head *pos;
+	
+	if (unlikely(!(fd && data->ready_to_queue)))
+		return false;
+	
+	char buf[0x100];
+	int queued_ok;
+	size_t qjs_sz = (32 + 12 + 2);
+	size_t qjp_sz = 4 + (qjs_sz * data->ready_to_queue);
+	uint8_t qjp[qjp_sz], *qjs;
+	qjp[0] = qjp_sz - 1;
+	qjp[1] = 0xc1;
+	qjp[2] = data->ready_to_queue;
+	qjp[qjp_sz - 1] = 0xfe;
+	qjs = &qjp[qjp_sz - 1];
+	
+	pos = thr->work_list.prev;
+	for (int i = data->ready_to_queue; i > 0; --i, pos = pos->prev)
+	{
+		work = list_entry(pos, typeof(*work), list);
+		*(--qjs) = 0xaa;
+		memcpy(qjs -= 12, work->data + 64, 12);
+		memcpy(qjs -= 32, work->midstate, 32);
+		*(--qjs) = 45;
+	}
+	
+	mutex_lock(mutexp);
+	bitforce_cmd2(fd, data->xlink_id, buf, sizeof(buf), "ZWX", qjp, qjp_sz);
+	mutex_unlock(mutexp);
+	
+	if (!strncasecmp(buf, "ERR:QUEUE", 9))
+	{
+		// Queue full :(
+		applog(LOG_DEBUG, "%"PRIpreprv": Device queue full while attempting to append %d jobs (queued<=%d)",
+	           bitforce->proc_repr,
+	           data->ready_to_queue, data->queued);
+		thr->queue_full = true;
+		return false;
+	}
+	if (strncasecmp(buf, "OK:QUEUED", 9))
+	{
+		// TODO: ERR:UNKNOWN COMMAND
+		applog(LOG_DEBUG, "%"PRIpreprv": Unexpected error attempting to append %d jobs (queued<=%d): %s",
+	           bitforce->proc_repr,
+	           data->ready_to_queue, data->queued, buf);
+		return false;
+	}
+	
+	queued_ok = atoi(&buf[9]);
+	data->queued += queued_ok;
+	applog(LOG_DEBUG, "%"PRIpreprv": Successfully queued %d/%d jobs on device (queued<=%d)",
+	       bitforce->proc_repr,
+	       queued_ok, data->ready_to_queue, data->queued);
+	data->ready_to_queue -= queued_ok;
+	thr->queue_full = data->ready_to_queue;
+	data->just_flushed = false;
+	
+	return true;
+}
+
+void work_list_del(struct list_head *pos)
+{
+	struct work *work;
+	
+	work = list_entry(pos, typeof(*work), list);
+	list_del(pos);
+	free_work(work);
+}
+
+static
+bool bitforce_queue_do_results(struct thr_info *thr)
+{
+	struct cgpu_info *bitforce = thr->cgpu;
+	struct bitforce_data *data = bitforce->cgpu_data;
+	int fd = bitforce->device->device_fd;
+	int count;
+	char *buf = &data->noncebuf[0];
+	unsigned char midstate[32], datatail[12];
+	struct work *work;
+	struct list_head *pos, *next_pos;
+	struct timeval tv_now, tv_elapsed;
+	
+	if (unlikely(!fd))
+		return false;
+	
+	count = bitforce_zox(thr, "ZOX");
+	
+	if (!count)
+		return true;
+	
+	if (unlikely(count < 0))
+	{
+		applog(LOG_ERR, "%"PRIpreprv": Received unexpected queue result response: %s", bitforce->proc_repr, buf);
+		++bitforce->hw_errors;
+		++hw_errors;
+		return false;
+	}
+	
+	if (unlikely(list_empty(&thr->work_list)))
+	{
+		applog(LOG_ERR, "%"PRIpreprv": Received %d queued results when there was no queue", bitforce->proc_repr, count);
+		++bitforce->hw_errors;
+		++hw_errors;
+		return true;
+	}
+	
+	if (count != BITFORCE_GOAL_QRESULTS)
+	{
+		unsigned int old_sleep_ms = bitforce->sleep_ms;
+		bitforce->sleep_ms = (uint32_t)bitforce->sleep_ms * BITFORCE_GOAL_QRESULTS / (count ?: 1);
+		applog(LOG_DEBUG, "%"PRIpreprv": Received %d queue results after %ums; Wait time changed to: %ums",
+		       bitforce->proc_repr, count, old_sleep_ms, bitforce->sleep_ms);
+	}
+	else
+		applog(LOG_DEBUG, "%"PRIpreprv": Received %d queue results after %ums; Wait time unchanged",
+		       bitforce->proc_repr, count, bitforce->sleep_ms);
+	
+	count = 0;
+	while ((buf = next_line(buf)), buf[0])
+	{
+		if (strlen(buf) <= 90)
+		{
+			applog(LOG_ERR, "%"PRIpreprv": Gibberish within queue results: %s", bitforce->proc_repr, buf);
+			continue;
+		}
+		
+		hex2bin(midstate, buf, 32);
+		hex2bin(datatail, &buf[65], 12);
+		
+		for (pos = thr->work_list.next; ; pos = pos->next)
+		{
+			if (unlikely(pos == &thr->work_list))
+			{
+				applog(LOG_ERR, "%"PRIpreprv": Failed to find work for queue results", bitforce->proc_repr);
+				++bitforce->hw_errors;
+				++hw_errors;
+				goto next_qline;
+			}
+			
+			work = list_entry(pos, typeof(*work), list);
+			if (unlikely(memcmp(work->midstate, midstate, 32)))
+				continue;
+			if (unlikely(memcmp(&work->data[64], datatail, 12)))
+				continue;
+			break;
+		}
+		
+		++count;
+		if (atoi(&buf[90]))
+			bitforce_process_result_nonces(thr, work, &buf[92]);
+		
+		// Queue results are in order, so anything queued prior this is lost
+		// Delete all queued work up to, and including, this one
+		for ( ; pos != &thr->work_list; pos = next_pos)
+		{
+			next_pos = pos->prev;
+			work_list_del(pos);
+			--data->queued;
+		}
+next_qline: (void)0;
+	}
+	
+	bitforce_set_queue_full(thr);
+	
+	gettimeofday(&tv_now, NULL);
+	timersub(&tv_now, &data->tv_hashmeter_start, &tv_elapsed);
+	hashes_done(thr, (uint64_t)bitforce->nonces * count, &tv_elapsed, NULL);
+	data->tv_hashmeter_start = tv_now;
+	
+	return true;
+}
+
+static
+bool bitforce_queue_append(struct thr_info *thr, struct work *work)
+{
+	struct cgpu_info *bitforce = thr->cgpu;
+	struct bitforce_data *data = bitforce->cgpu_data;
+	bool rv, ndq;
+	
+	bitforce_set_queue_full(thr);
+	rv = !thr->queue_full;
+	if (rv)
+	{
+		list_add_tail(&work->list, &thr->work_list);
+		++data->ready_to_queue;
+		applog(LOG_DEBUG, "%"PRIpreprv": Appending to driver queue (max=%u, ready=%d, queued<=%d)",
+		       bitforce->proc_repr,
+		       (unsigned)BITFORCE_MAX_QUEUED, data->ready_to_queue, data->queued);
+		bitforce_set_queue_full(thr);
+	}
+	
+	ndq = !data->queued;
+	if ((ndq)              // Device is idle
+	 || (data->ready_to_queue >= 5)  // ...or 5 items ready to go
+	 || (thr->queue_full)            // ...or done filling queue
+	 || (data->just_flushed)         // ...or queue was just flushed (only remaining job is partly done already)
+	)
+	{
+		bitforce_send_queue(thr);
+		if (ndq)
+			gettimeofday(&data->tv_hashmeter_start, NULL);
+	}
+	
+	return rv;
+}
+
+static
+void bitforce_queue_flush(struct thr_info *thr)
+{
+	struct cgpu_info *bitforce = thr->cgpu;
+	struct bitforce_data *data = bitforce->cgpu_data;
+	pthread_mutex_t *mutexp = &bitforce->device->device_mutex;
+	int fd = bitforce->device->device_fd;
+	char buf[100];
+	unsigned flushed;
+	
+	mutex_lock(mutexp);
+	bitforce_cmd1(fd, data->xlink_id, buf, sizeof(buf), "ZQX");
+	mutex_unlock(mutexp);
+	if (unlikely(strncasecmp(buf, "OK:FLUSHED", 10)))
+	{
+		applog(LOG_DEBUG, "%"PRIpreprv": Failed to flush device queue: %s", bitforce->proc_repr, buf);
+		flushed = 0;
+	}
+	else
+		flushed = atoi(&buf[10]);
+	
+	applog(LOG_DEBUG, "%"PRIpreprv": Flushed %u jobs from device and %d from driver",
+	       bitforce->proc_repr, flushed, data->ready_to_queue);
+	
+	data->queued -= flushed;
+	flushed += data->ready_to_queue;
+	data->ready_to_queue = 0;
+	while (flushed--)
+		work_list_del(thr->work_list.prev);
+	thr->queue_full = false;
+	data->just_flushed = true;
+	
+	bitforce_queue_do_results(thr);
+}
+
+static
+void bitforce_queue_poll(struct thr_info *thr)
+{
+	struct cgpu_info *bitforce = thr->cgpu;
+	
+	bitforce_queue_do_results(thr);
+	timer_set_delay_from_now(&thr->tv_poll, bitforce->sleep_ms * 1000);
+}
+
+struct device_api bitforce_queue_api = {
+	.dname = "bitforce_queue",
+	.name = "BFL",
+	.minerloop = minerloop_queue,
+	.reinit_device = bitforce_init,
+	.get_statline_before = get_bitforce_statline_before,
+	.get_stats = bitforce_get_stats,
+	.identify_device = bitforce_identify,
+	.thread_prepare = bitforce_thread_prepare,
+	.thread_init = bitforce_thread_init,
+	.queue_append = bitforce_queue_append,
+	.queue_flush = bitforce_queue_flush,
+	.poll = bitforce_queue_poll,
+	.thread_shutdown = bitforce_shutdown,
+	.thread_enable = biforce_thread_enable
+};

+ 1 - 0
miner.c

@@ -8357,6 +8357,7 @@ begin_bench:
 			thr->mutex_request[1] = INVSOCK;
 			thr->mutex_request[1] = INVSOCK;
 			thr->_job_transition_in_progress = true;
 			thr->_job_transition_in_progress = true;
 			timerclear(&thr->tv_morework);
 			timerclear(&thr->tv_morework);
+			thr->_last_sbr_state = true;
 
 
 			thr->scanhash_working = true;
 			thr->scanhash_working = true;
 			thr->hashes_done = 0;
 			thr->hashes_done = 0;

+ 11 - 4
miner.h

@@ -297,16 +297,18 @@ struct device_api {
 	void (*thread_shutdown)(struct thr_info *);
 	void (*thread_shutdown)(struct thr_info *);
 	void (*thread_enable)(struct thr_info *);
 	void (*thread_enable)(struct thr_info *);
 
 
-	// === Implemented by minerloop_async ===
-
-	// Can be used per-thread or per-processor
+	// Can be used per-thread or per-processor (only with minerloop async or queue!)
 	void (*poll)(struct thr_info *);
 	void (*poll)(struct thr_info *);
 
 
-	// Job-specific functions (only with minerloop_async!)
+	// === Implemented by minerloop_async ===
 	bool (*job_prepare)(struct thr_info*, struct work*, uint64_t);
 	bool (*job_prepare)(struct thr_info*, struct work*, uint64_t);
 	void (*job_start)(struct thr_info*);
 	void (*job_start)(struct thr_info*);
 	void (*job_get_results)(struct thr_info*, struct work*);
 	void (*job_get_results)(struct thr_info*, struct work*);
 	int64_t (*job_process_results)(struct thr_info*, struct work*, bool stopping);
 	int64_t (*job_process_results)(struct thr_info*, struct work*, bool stopping);
+
+	// === Implemented by minerloop_queue ===
+	bool (*queue_append)(struct thr_info *, struct work *);
+	void (*queue_flush)(struct thr_info *);
 };
 };
 
 
 enum dev_enable {
 enum dev_enable {
@@ -579,6 +581,11 @@ struct thr_info {
 	uint32_t _max_nonce;
 	uint32_t _max_nonce;
 	notifier_t mutex_request;
 	notifier_t mutex_request;
 
 
+	// Used by minerloop_queue
+	struct list_head work_list;
+	bool queue_full;
+	bool _last_sbr_state;
+
 	bool	work_restart;
 	bool	work_restart;
 	notifier_t work_restart_notifier;
 	notifier_t work_restart_notifier;
 };
 };