Browse Source

Working processor disable/enable with new async minerloop (currently gets stuck if all processors disabled)

Luke Dashjr 13 years ago
parent
commit
8be3fb102a
4 changed files with 136 additions and 49 deletions
  1. 54 10
      driver-x6500.c
  2. 76 39
      miner.c
  3. 4 0
      miner.h
  4. 2 0
      util.h

+ 54 - 10
driver-x6500.c

@@ -126,6 +126,7 @@ static bool x6500_foundusb(libusb_device *dev, const char *product, const char *
 	struct cgpu_info *x6500;
 	struct cgpu_info *x6500;
 	x6500 = calloc(1, sizeof(*x6500));
 	x6500 = calloc(1, sizeof(*x6500));
 	x6500->api = &x6500_api;
 	x6500->api = &x6500_api;
+	mutex_init(&x6500->device_mutex);
 	x6500->device_path = strdup(serial);
 	x6500->device_path = strdup(serial);
 	x6500->deven = DEV_ENABLED;
 	x6500->deven = DEV_ENABLED;
 	x6500->threads = 1;
 	x6500->threads = 1;
@@ -188,6 +189,7 @@ static bool x6500_prepare(struct thr_info *thr)
 struct x6500_fpga_data {
 struct x6500_fpga_data {
 	struct jtag_port jtag;
 	struct jtag_port jtag;
 	struct timeval tv_hashstart;
 	struct timeval tv_hashstart;
+	int64_t hashes_left;
 
 
 	struct dclk_data dclk;
 	struct dclk_data dclk;
 	uint8_t freqMaxMaxM;
 	uint8_t freqMaxMaxM;
@@ -510,13 +512,25 @@ void x6500_get_temperature(struct cgpu_info *x6500)
 
 
 }
 }
 
 
+static
+bool x6500_all_idle(struct cgpu_info *any_proc)
+{
+	for (struct cgpu_info *proc = any_proc->device; proc; proc = proc->next_proc)
+		if (proc->thr[0]->tv_poll.tv_sec != -1)
+			return false;
+	return true;
+}
+
 static bool x6500_get_stats(struct cgpu_info *x6500)
 static bool x6500_get_stats(struct cgpu_info *x6500)
 {
 {
 	float hottest = 0;
 	float hottest = 0;
-	if (x6500->deven != DEV_ENABLED) {
-		// Getting temperature more efficiently while enabled
-		// FIXME: Move this to minerloop
-		x6500_get_temperature(x6500);
+	if (x6500_all_idle(x6500)) {
+		// Getting temperature more efficiently while running
+		pthread_mutex_t *mutexp = &x6500->device->device_mutex;
+		mutex_lock(mutexp);
+		if (x6500_all_idle(x6500))
+			x6500_get_temperature(x6500);
+		mutex_unlock(mutexp);
 	}
 	}
 
 
 	for (int i = x6500->threads; i--; ) {
 	for (int i = x6500->threads; i--; ) {
@@ -613,6 +627,13 @@ bool x6500_job_prepare(struct thr_info *thr, struct work *work, __maybe_unused u
 	struct x6500_fpga_data *fpga = thr->cgpu_data;
 	struct x6500_fpga_data *fpga = thr->cgpu_data;
 	struct jtag_port *jp = &fpga->jtag;
 	struct jtag_port *jp = &fpga->jtag;
 
 
+	if (x6500_all_idle(x6500))
+	{
+		// Neither FPGA is running, so make sure we're not doing a temperature check
+		pthread_mutex_t *mutexp = &x6500->device->device_mutex;
+		mutex_lock(mutexp);
+	}
+	
 	for (int i = 1, j = 0; i < 9; ++i, j += 4)
 	for (int i = 1, j = 0; i < 9; ++i, j += 4)
 		x6500_set_register(jp, i, fromlebytes(work->midstate, j));
 		x6500_set_register(jp, i, fromlebytes(work->midstate, j));
 
 
@@ -630,6 +651,8 @@ bool x6500_job_prepare(struct thr_info *thr, struct work *work, __maybe_unused u
 	return true;
 	return true;
 }
 }
 
 
+static int64_t calc_hashes(struct thr_info *, struct timeval *);
+
 static
 static
 void x6500_job_start(struct thr_info *thr)
 void x6500_job_start(struct thr_info *thr)
 {
 {
@@ -649,9 +672,20 @@ void x6500_job_start(struct thr_info *thr)
 	ft232r_flush(jp->a->ftdi);
 	ft232r_flush(jp->a->ftdi);
 
 
 	gettimeofday(&tv_now, NULL);
 	gettimeofday(&tv_now, NULL);
-	if (!thr->prev_work)
+	if (!thr->work)
 		fpga->tv_hashstart = tv_now;
 		fpga->tv_hashstart = tv_now;
+	else
+	if (thr->work != thr->next_work)
+		calc_hashes(thr, &tv_now);
+	fpga->hashes_left = 0x100000000;
 
 
+	if (x6500_all_idle(x6500))
+	{
+		pthread_mutex_t *mutexp = &x6500->device->device_mutex;
+		thr->work = (void*)1;  // HACK: Should be replaced immediately upon return
+		mutex_unlock(mutexp);
+	}
+	
 	if (opt_debug) {
 	if (opt_debug) {
 		char *xdata = bin2hex(thr->next_work->data, 80);
 		char *xdata = bin2hex(thr->next_work->data, 80);
 		applog(LOG_DEBUG, "%"PRIprepr": Started work: %s",
 		applog(LOG_DEBUG, "%"PRIprepr": Started work: %s",
@@ -671,12 +705,14 @@ int64_t calc_hashes(struct thr_info *thr, struct timeval *tv_now)
 {
 {
 	struct x6500_fpga_data *fpga = thr->cgpu_data;
 	struct x6500_fpga_data *fpga = thr->cgpu_data;
 	struct timeval tv_delta;
 	struct timeval tv_delta;
-	int64_t hashes;
+	int64_t hashes, hashes_left;
 
 
 	timersub(tv_now, &fpga->tv_hashstart, &tv_delta);
 	timersub(tv_now, &fpga->tv_hashstart, &tv_delta);
 	hashes = (((int64_t)tv_delta.tv_sec * 1000000) + tv_delta.tv_usec) * fpga->dclk.freqM * 2;
 	hashes = (((int64_t)tv_delta.tv_sec * 1000000) + tv_delta.tv_usec) * fpga->dclk.freqM * 2;
-	if (unlikely(hashes > 0x100000000))
-		hashes = 0x100000000;
+	hashes_left = fpga->hashes_left;
+	if (unlikely(hashes > hashes_left))
+		hashes = hashes_left;
+	fpga->hashes_left -= hashes;
 	hashes_done(thr, hashes, &tv_delta, NULL);
 	hashes_done(thr, hashes, &tv_delta, NULL);
 	fpga->tv_hashstart = *tv_now;
 	fpga->tv_hashstart = *tv_now;
 	return hashes;
 	return hashes;
@@ -698,7 +734,7 @@ int64_t x6500_process_results(struct thr_info *thr, struct work *work)
 		gettimeofday(&tv_now, NULL);
 		gettimeofday(&tv_now, NULL);
 		nonce = x6500_get_register(jtag, 0xE);
 		nonce = x6500_get_register(jtag, 0xE);
 		if (nonce != 0xffffffff) {
 		if (nonce != 0xffffffff) {
-			bad = !test_nonce(work, nonce, false);
+			bad = !(work && test_nonce(work, nonce, false));
 			if (!bad) {
 			if (!bad) {
 				submit_nonce(thr, work, nonce);
 				submit_nonce(thr, work, nonce);
 				applog(LOG_DEBUG, "%"PRIprepr": Nonce for current  work: %08lx",
 				applog(LOG_DEBUG, "%"PRIprepr": Nonce for current  work: %08lx",
@@ -737,8 +773,16 @@ int64_t x6500_process_results(struct thr_info *thr, struct work *work)
 static
 static
 void x6500_fpga_poll(struct thr_info *thr)
 void x6500_fpga_poll(struct thr_info *thr)
 {
 {
+	struct x6500_fpga_data *fpga = thr->cgpu_data;
+	
 	x6500_process_results(thr, thr->work);
 	x6500_process_results(thr, thr->work);
-	timer_set_delay_from_now(&thr->tv_poll, 10000);
+	if (unlikely(!fpga->hashes_left))
+	{
+		mt_disable_start(thr);
+		thr->tv_poll.tv_sec = -1;
+	}
+	else
+		timer_set_delay_from_now(&thr->tv_poll, 10000);
 }
 }
 
 
 struct device_api x6500_api = {
 struct device_api x6500_api = {

+ 76 - 39
miner.c

@@ -6598,13 +6598,21 @@ bool abandon_work(struct work *work, struct timeval *wdiff, uint64_t hashes)
 	return false;
 	return false;
 }
 }
 
 
-static void mt_disable(struct thr_info *mythr, const int thr_id,
-		       const struct device_api *api)
+void mt_disable_start(struct thr_info *mythr)
 {
 {
+	int thr_id = mythr->id;
+	
 	applog(LOG_WARNING, "Thread %d being disabled", thr_id);
 	applog(LOG_WARNING, "Thread %d being disabled", thr_id);
 	mythr->rolling = mythr->cgpu->rolling = 0;
 	mythr->rolling = mythr->cgpu->rolling = 0;
-	applog(LOG_DEBUG, "Popping wakeup ping in miner thread");
 	thread_reportout(mythr);
 	thread_reportout(mythr);
+}
+
+void mt_disable_finish(struct thr_info *mythr)
+{
+	int thr_id = mythr->id;
+	const struct device_api *api = mythr->cgpu->api;
+	
+	applog(LOG_DEBUG, "Popping wakeup ping in miner thread");
 	do {
 	do {
 		tq_pop(mythr->q, NULL); /* Ignore ping that's popped */
 		tq_pop(mythr->q, NULL); /* Ignore ping that's popped */
 	} while (mythr->pause);
 	} while (mythr->pause);
@@ -6614,6 +6622,14 @@ static void mt_disable(struct thr_info *mythr, const int thr_id,
 		api->thread_enable(mythr);
 		api->thread_enable(mythr);
 }
 }
 
 
+static
+void mt_disable(struct thr_info *mythr, __maybe_unused const int thr_id,
+		       __maybe_unused const struct device_api *api)
+{
+	mt_disable_start(mythr);
+	mt_disable_finish(mythr);
+}
+
 bool hashes_done(struct thr_info *thr, int64_t hashes, struct timeval *tvp_hashes, uint32_t *max_nonce)
 bool hashes_done(struct thr_info *thr, int64_t hashes, struct timeval *tvp_hashes, uint32_t *max_nonce)
 {
 {
 	struct cgpu_info *cgpu = thr->cgpu;
 	struct cgpu_info *cgpu = thr->cgpu;
@@ -6763,33 +6779,48 @@ void minerloop_async(struct thr_info *mythr)
 		{
 		{
 			mythr = proc->thr[0];
 			mythr = proc->thr[0];
 			
 			
-			if (unlikely(mythr->work_restart) || timercmp(&mythr->tv_morework, &tv_now, <))
+			bool was_disabled = (mythr->tv_morework.tv_sec == -1);
+			
+			if (was_disabled
+			     ? (proc->deven == DEV_ENABLED && !mythr->pause)
+			     : (
+			           unlikely(mythr->work_restart)
+			        || timercmp(&mythr->tv_morework, &tv_now, <)
+			       )
+			   )
 			{
 			{
+				if (was_disabled)
+					mt_disable_finish(mythr);
+disabled: ;
+				bool keepgoing = (proc->deven == DEV_ENABLED && !mythr->pause);
 				thread_reportin(mythr);
 				thread_reportin(mythr);
 				prev_job_work = mythr->work;
 				prev_job_work = mythr->work;
 				tv_start = mythr->tv_jobstart;
 				tv_start = mythr->tv_jobstart;
-				if (prev_job_work)
-					timersub(&tv_now, &mythr->work->tv_work_start, &tv_worktime);
-				if ((!prev_job_work) || abandon_work(mythr->work, &tv_worktime, cgpu->max_hashes))
+				if (likely(keepgoing))
 				{
 				{
-					mythr->work_restart = false;
-					request_work(mythr);
-					// FIXME: Allow get_work to return NULL to retry on notification
-					mythr->next_work = get_work(mythr);
-					if (api->prepare_work && !api->prepare_work(mythr, mythr->next_work)) {
-						applog(LOG_ERR, "work prepare failed, exiting "
-							"mining thread %d", thr_id);
-						break;  // FIXME
+					if (prev_job_work)
+						timersub(&tv_now, &mythr->work->tv_work_start, &tv_worktime);
+					if ((!prev_job_work) || abandon_work(mythr->work, &tv_worktime, cgpu->max_hashes))
+					{
+						mythr->work_restart = false;
+						request_work(mythr);
+						// FIXME: Allow get_work to return NULL to retry on notification
+						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->deven = DEV_RECOVER_ERR;
+							goto disabled;
+						}
+						starting_new_work = true;
+						new_job_work = mythr->next_work;
 					}
 					}
-					starting_new_work = true;
-					new_job_work = mythr->next_work;
-				}
-				else
-				{
-					starting_new_work = false;
-					new_job_work = mythr->work;
+					else
+					{
+						starting_new_work = false;
+						new_job_work = mythr->work;
+					}
+					api->job_prepare(mythr, new_job_work, max_nonce);
 				}
 				}
-				api->job_prepare(mythr, new_job_work, max_nonce);
 				if (likely(prev_job_work))
 				if (likely(prev_job_work))
 				{
 				{
 					hashes = -101;
 					hashes = -101;
@@ -6797,17 +6828,26 @@ void minerloop_async(struct thr_info *mythr)
 						hashes = api->job_get_results(mythr, prev_job_work);
 						hashes = api->job_get_results(mythr, prev_job_work);
 				}
 				}
 				gettimeofday(&tv_now, NULL);  // NOTE: Can go away when fully async
 				gettimeofday(&tv_now, NULL);  // NOTE: Can go away when fully async
-				if (starting_new_work)
-					mythr->next_work->tv_work_start = tv_now;
-				mythr->tv_jobstart = tv_now;
-				api->job_start(mythr);
-				if (starting_new_work)
+				if (likely(keepgoing))
+				{
+					if (starting_new_work)
+						mythr->next_work->tv_work_start = tv_now;
+					mythr->tv_jobstart = tv_now;
+					api->job_start(mythr);
+					if (starting_new_work)
+					{
+						if (mythr->prev_work)
+							free_work(mythr->prev_work);
+						mythr->prev_work = prev_job_work;
+						mythr->work = mythr->next_work;
+						mythr->next_work = NULL;
+					}
+				}
+				else
 				{
 				{
-					if (mythr->prev_work)
-						free_work(mythr->prev_work);
 					mythr->prev_work = mythr->work;
 					mythr->prev_work = mythr->work;
-					mythr->work = mythr->next_work;
-					mythr->next_work = NULL;
+					mythr->work = NULL;
+					mythr->tv_morework.tv_sec = -1;
 				}
 				}
 				if (likely(prev_job_work))
 				if (likely(prev_job_work))
 				{
 				{
@@ -6819,12 +6859,12 @@ void minerloop_async(struct thr_info *mythr)
 					{
 					{
 						timersub(&tv_now, &tv_start, &tv_hashes);
 						timersub(&tv_now, &tv_start, &tv_hashes);
 						if (!hashes_done(mythr, hashes, &tv_hashes, api->can_limit_work ? &max_nonce : NULL))
 						if (!hashes_done(mythr, hashes, &tv_hashes, api->can_limit_work ? &max_nonce : NULL))
-							break;  // FIXME: Disable the processor
+							goto disabled;
 					}
 					}
 				}
 				}
 			}
 			}
 			
 			
-			if (timercmp(&mythr->tv_poll, &tv_now, <))
+			if (mythr->tv_poll.tv_sec != -1 && timercmp(&mythr->tv_poll, &tv_now, <))
 				api->poll(mythr);
 				api->poll(mythr);
 			
 			
 			reduce_timeout_to(&tv_timeout, &mythr->tv_morework);
 			reduce_timeout_to(&tv_timeout, &mythr->tv_morework);
@@ -6844,11 +6884,6 @@ void minerloop_async(struct thr_info *mythr)
 		}
 		}
 		// FIXME: break select on work restart
 		// FIXME: break select on work restart
 		select(0, NULL, NULL, NULL, tvp_timeout);
 		select(0, NULL, NULL, NULL, tvp_timeout);
-#if 0
-			if (unlikely(mythr->pause || cgpu->deven != DEV_ENABLED))
-disabled:
-				mt_disable(mythr, thr_id, api);
-#endif
 	}
 	}
 }
 }
 
 
@@ -8445,6 +8480,8 @@ begin_bench:
 	// Start threads
 	// Start threads
 	for (i = 0; i < total_devices; ++i) {
 	for (i = 0; i < total_devices; ++i) {
 		struct cgpu_info *cgpu = devices[i];
 		struct cgpu_info *cgpu = devices[i];
+		if (!cgpu->threads)
+			cgpu->thr[0]->q = cgpu->device->thr[0]->q;
 		for (j = 0; j < cgpu->threads; ++j) {
 		for (j = 0; j < cgpu->threads; ++j) {
 			thr = cgpu->thr[j];
 			thr = cgpu->thr[j];
 
 

+ 4 - 0
miner.h

@@ -406,12 +406,14 @@ struct cgpu_info {
 	char *dev_repr;
 	char *dev_repr;
 	char *dev_repr_ns;
 	char *dev_repr_ns;
 	const char *name;
 	const char *name;
+	
 	int procs;
 	int procs;
 	int proc_id;
 	int proc_id;
 	char proc_repr[8];
 	char proc_repr[8];
 	char proc_repr_ns[8];
 	char proc_repr_ns[8];
 	struct cgpu_info *device;
 	struct cgpu_info *device;
 	struct cgpu_info *next_proc;
 	struct cgpu_info *next_proc;
+	
 	const char *device_path;
 	const char *device_path;
 	FILE *device_file;
 	FILE *device_file;
 	union {
 	union {
@@ -1113,6 +1115,8 @@ extern enum test_nonce2_result _test_nonce2(struct work *, uint32_t nonce, bool
 extern void submit_nonce(struct thr_info *thr, struct work *work, uint32_t nonce);
 extern void submit_nonce(struct thr_info *thr, struct work *work, uint32_t nonce);
 extern bool abandon_work(struct work *, struct timeval *work_runtime, uint64_t hashes);
 extern bool abandon_work(struct work *, struct timeval *work_runtime, uint64_t hashes);
 extern bool hashes_done(struct thr_info *, int64_t hashes, struct timeval *tvp_hashes, uint32_t *max_nonce);
 extern bool hashes_done(struct thr_info *, int64_t hashes, struct timeval *tvp_hashes, uint32_t *max_nonce);
+extern void mt_disable_start(struct thr_info *);
+extern void mt_disable_finish(struct thr_info *);
 extern void tailsprintf(char *f, const char *fmt, ...);
 extern void tailsprintf(char *f, const char *fmt, ...);
 extern void wlogprint(const char *f, ...);
 extern void wlogprint(const char *f, ...);
 extern int curses_int(const char *query);
 extern int curses_int(const char *query);

+ 2 - 0
util.h

@@ -99,6 +99,8 @@ static inline void align_len(size_t *len)
 static inline
 static inline
 void reduce_timeout_to(struct timeval *tvp_timeout, struct timeval *tvp_time)
 void reduce_timeout_to(struct timeval *tvp_timeout, struct timeval *tvp_time)
 {
 {
+	if (tvp_time->tv_sec == -1)
+		return;
 	if (tvp_timeout->tv_sec == -1 /* no timeout */ || timercmp(tvp_time, tvp_timeout, <))
 	if (tvp_timeout->tv_sec == -1 /* no timeout */ || timercmp(tvp_time, tvp_timeout, <))
 		*tvp_timeout = *tvp_time;
 		*tvp_timeout = *tvp_time;
 }
 }