Browse Source

Make minerloop_async more async, using some callbacks to handle event completions

Luke Dashjr 13 years ago
parent
commit
1d06401a40
5 changed files with 133 additions and 46 deletions
  1. 9 3
      HACKING
  2. 3 0
      driver-x6500.c
  3. 101 42
      miner.c
  4. 14 1
      miner.h
  5. 6 0
      util.h

+ 9 - 3
HACKING

@@ -87,6 +87,8 @@ called. This is given the Processor `struct thr_info *` as its only argument,
 and should start the job most recently prepared with `job_prepare`. Note that
 it is possible for `job_prepare` to be called for a job that never starts
 (another `job_prepare` may be executed to override the previous one instead).
+`job_start` must call `mt_job_transition` as soon as the actual switchover to
+the new job takes place, and must call `job_start_complete` when finished.
 `job_start` must set `thr->tv_morework` to the time the device expects to need
 its next work item. It is generally advisable to set this a bit early to ensure
 any delays do not make it late. `job_start` is expected to always succeed and
@@ -96,9 +98,13 @@ Immediately before `job_start` is called to change from one job to the next,
 `job_get_results` will be called to fetch any volatile results from the
 previous job. It is provided the Processor's `struct thr_info *` and the
 currently executing job's `struct work *`. It should ONLY fetch the raw data
-for the results, and not spend any time processing or submitting it. After the
-new job has been started, your driver's `job_process_results` function will be
-called with the same arguments to complete the submission of these results.
+for the results, and not spend any time processing or submitting it. If
+`job_get_results` is defined for your driver, it must (directly or indirectly)
+ensure `job_results_fetched` is called when complete. After the new job has
+been started, your driver's `job_process_results` function will be called to
+complete the submission of these results with the same arguments, plus a bool
+to tell you whether the processor is being stopped. If it is, your driver must
+call `mt_disable_start` when it has successfully stopped hashing.
 
 Drivers may define a `poll` function. If this is defined, `thr->tv_poll` must
 always be set to a valid time to next execute it, for each Processor.

+ 3 - 0
driver-x6500.c

@@ -678,6 +678,7 @@ void x6500_job_start(struct thr_info *thr)
 	if (thr->prev_work != thr->work)
 		calc_hashes(thr, &tv_now);
 	fpga->hashes_left = 0x100000000;
+	mt_job_transition(thr);
 
 	if (x6500_all_idle(x6500))
 	{
@@ -697,6 +698,8 @@ void x6500_job_start(struct thr_info *thr)
 	timer_set_delay(&thr->tv_morework, &tv_now, usecs);
 
 	timer_set_delay(&thr->tv_poll, &tv_now, 10000);
+	
+	job_start_complete(thr);
 }
 
 static

+ 101 - 42
miner.c

@@ -6626,6 +6626,9 @@ void mt_disable_start(struct thr_info *mythr)
 	int thr_id = mythr->id;
 	
 	hashmeter2(mythr);
+	mythr->prev_work = mythr->work;
+	mythr->work = NULL;
+	mythr->_job_transition_in_progress = false;
 	applog(LOG_WARNING, "Thread %d being disabled", thr_id);
 	mythr->rolling = mythr->cgpu->rolling = 0;
 	thread_reportout(mythr);
@@ -6773,6 +6776,8 @@ bool do_job_prepare(struct thr_info *mythr, struct timeval *tvp_now)
 	const struct device_api *api = proc->api;
 	struct timeval tv_worktime;
 	
+	mythr->tv_morework.tv_sec = -1;
+	mythr->_job_transition_in_progress = true;
 	if (mythr->work)
 		timersub(tvp_now, &mythr->work->tv_work_start, &tv_worktime);
 	if ((!mythr->work) || abandon_work(mythr->work, &tv_worktime, proc->max_hashes))
@@ -6794,39 +6799,96 @@ bool do_job_prepare(struct thr_info *mythr, struct timeval *tvp_now)
 		mythr->starting_next_work = false;
 		api->job_prepare(mythr, mythr->work, mythr->_max_nonce);
 	}
+	job_prepare_complete(mythr);
 	return true;
 }
 
-void do_job_start(struct thr_info *mythr, struct timeval *tvp_now)
+void job_prepare_complete(struct thr_info *mythr)
+{
+	if (mythr->work)
+	{
+		if (true /* TODO: job is near complete */ || unlikely(mythr->work_restart))
+			do_get_results(mythr, true);
+		else
+		{}  // TODO: Set a timer to call do_get_results when job is near complete
+	}
+	else  // no job currently running
+		do_job_start(mythr);
+}
+
+void do_get_results(struct thr_info *mythr, bool proceed_with_new_job)
+{
+	struct cgpu_info *proc = mythr->cgpu;
+	const struct device_api *api = proc->api;
+	struct work *work = mythr->work;
+	
+	mythr->_job_transition_in_progress = true;
+	mythr->tv_results_jobstart = mythr->tv_jobstart;
+	mythr->_proceed_with_new_job = proceed_with_new_job;
+	if (api->job_get_results)
+		api->job_get_results(mythr, work);
+	else
+		job_results_fetched(mythr);
+}
+
+void job_results_fetched(struct thr_info *mythr)
+{
+	if (mythr->_proceed_with_new_job)
+		do_job_start(mythr);
+	else
+	{
+		struct timeval tv_now;
+		
+		gettimeofday(&tv_now, NULL);
+		
+		do_process_results(mythr, &tv_now, mythr->prev_work, true);
+	}
+}
+
+void do_job_start(struct thr_info *mythr)
 {
 	struct cgpu_info *proc = mythr->cgpu;
 	const struct device_api *api = proc->api;
 	
+	thread_reportin(mythr);
+	api->job_start(mythr);
+}
+
+void mt_job_transition(struct thr_info *mythr)
+{
+	struct timeval tv_now;
+	
+	gettimeofday(&tv_now, NULL);
+	
 	if (mythr->starting_next_work)
 	{
-		mythr->next_work->tv_work_start = *tvp_now;
+		mythr->next_work->tv_work_start = tv_now;
 		if (mythr->prev_work)
 			free_work(mythr->prev_work);
 		mythr->prev_work = mythr->work;
 		mythr->work = mythr->next_work;
 		mythr->next_work = NULL;
 	}
-	mythr->tv_jobstart = *tvp_now;
-	thread_reportin(mythr);
-	api->job_start(mythr);
+	mythr->tv_jobstart = tv_now;
+	mythr->_job_transition_in_progress = false;
 }
 
-void do_get_results(struct thr_info *mythr, __maybe_unused struct timeval *tvp_now, struct work *work)
+void job_start_complete(struct thr_info *mythr)
 {
-	struct cgpu_info *proc = mythr->cgpu;
-	const struct device_api *api = proc->api;
+	struct timeval tv_now;
 	
-	if (api->job_get_results)
-		api->job_get_results(mythr, work);
-	mythr->tv_results_jobstart = mythr->tv_jobstart;
+	gettimeofday(&tv_now, NULL);
+	
+	if (!do_process_results(mythr, &tv_now, mythr->prev_work, false))
+	{
+		struct cgpu_info *proc = mythr->cgpu;
+		
+		proc->deven = DEV_RECOVER_ERR;
+		mythr->_job_transition_in_progress = false;
+	}
 }
 
-bool do_process_results(struct thr_info *mythr, struct timeval *tvp_now, struct work *work)
+bool do_process_results(struct thr_info *mythr, struct timeval *tvp_now, struct work *work, bool stopping)
 {
 	struct cgpu_info *proc = mythr->cgpu;
 	const struct device_api *api = proc->api;
@@ -6834,7 +6896,7 @@ bool do_process_results(struct thr_info *mythr, struct timeval *tvp_now, struct
 	int64_t hashes = 0;
 	
 	if (api->job_process_results)
-		hashes = api->job_process_results(mythr, work);
+		hashes = api->job_process_results(mythr, work, stopping);
 	thread_reportin(mythr);
 	
 	if (hashes)
@@ -6862,6 +6924,7 @@ void minerloop_async(struct thr_info *mythr)
 	int proc_thr_no;
 	int maxfd;
 	fd_set rfds;
+	bool is_running, should_be_running;
 	
 	while (1) {
 		tv_timeout.tv_sec = -1;
@@ -6869,49 +6932,44 @@ void minerloop_async(struct thr_info *mythr)
 		for (proc = cgpu; proc; proc = proc->next_proc)
 		{
 			mythr = proc->thr[0];
+			is_running = mythr->work;
+			should_be_running = (proc->deven == DEV_ENABLED && !mythr->pause);
 			
-			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 (should_be_running)
 			{
-				if (was_disabled)
+				if (unlikely(!(is_running || mythr->_job_transition_in_progress)))
+				{
 					mt_disable_finish(mythr);
-disabled: ;
-				bool keepgoing = (proc->deven == DEV_ENABLED && !mythr->pause);
-				prev_job_work = mythr->work;
-				if (likely(keepgoing))
-					if (!do_job_prepare(mythr, &tv_now))
-						goto disabled;
-				if (likely(prev_job_work))
-					do_get_results(mythr, &tv_now, prev_job_work);
-				gettimeofday(&tv_now, NULL);  // NOTE: Can go away when fully async
-				if (likely(keepgoing))
-					do_job_start(mythr, &tv_now);
-				else
+					goto djp;
+				}
+				if (unlikely(mythr->work_restart))
+					goto djp;
+			}
+			else  // ! should_be_running
+			{
+				if (unlikely(is_running && !mythr->_job_transition_in_progress))
 				{
-					mythr->prev_work = mythr->work;
-					mythr->work = NULL;
+disabled: ;
 					mythr->tv_morework.tv_sec = -1;
+					do_get_results(mythr, false);
 				}
-				if (likely(prev_job_work))
-					if (!do_process_results(mythr, &tv_now, prev_job_work))
-						goto disabled;
 			}
 			
-			if (mythr->tv_poll.tv_sec != -1 && timercmp(&mythr->tv_poll, &tv_now, <))
+			if (timer_passed(&mythr->tv_morework, &tv_now))
+			{
+djp: ;
+				if (!do_job_prepare(mythr, &tv_now))
+					goto disabled;
+			}
+			
+			if (timer_passed(&mythr->tv_poll, &tv_now))
 				api->poll(mythr);
 			
 			reduce_timeout_to(&tv_timeout, &mythr->tv_morework);
 			reduce_timeout_to(&tv_timeout, &mythr->tv_poll);
 		}
 		
-		gettimeofday(&tv_now, NULL);  // NOTE: Can go away when fully async
+		gettimeofday(&tv_now, NULL);
 		// FIXME: break select on work restart
 		FD_ZERO(&rfds);
 		FD_SET(mythr->notifier[0], &rfds);
@@ -8507,6 +8565,7 @@ begin_bench:
 			thr->cgpu = cgpu;
 			thr->device_thread = j;
 			thr->work_restart_fd = thr->_work_restart_fd_w = -1;
+			thr->_job_transition_in_progress = true;
 			timerclear(&thr->tv_morework);
 
 			thr->scanhash_working = true;

+ 14 - 1
miner.h

@@ -305,7 +305,7 @@ struct device_api {
 	bool (*job_prepare)(struct thr_info*, struct work*, uint64_t);
 	void (*job_start)(struct thr_info*);
 	void (*job_get_results)(struct thr_info*, struct work*);
-	int64_t (*job_process_results)(struct thr_info*, struct work*);  // return value ignored if job_get_results is used
+	int64_t (*job_process_results)(struct thr_info*, struct work*, bool stopping);
 };
 
 enum dev_enable {
@@ -560,6 +560,9 @@ struct thr_info {
 	struct work *work;
 	struct work *next_work;
 	struct timeval tv_morework;
+	struct work *results_work;
+	bool _job_transition_in_progress;
+	bool _proceed_with_new_job;
 	struct timeval tv_results_jobstart;
 	struct timeval tv_jobstart;
 	struct timeval tv_poll;
@@ -1112,7 +1115,17 @@ enum test_nonce2_result {
 	TNR_BAD,
 };
 extern void minerloop_scanhash(struct thr_info *);
+
+extern bool do_job_prepare(struct thr_info *, struct timeval *tvp_now);
+extern void job_prepare_complete(struct thr_info *);
+extern void do_get_results(struct thr_info *, bool proceed_with_new_job);
+extern void job_results_fetched(struct thr_info *);
+extern void do_job_start(struct thr_info *);
+extern void mt_job_transition(struct thr_info *);
+extern void job_start_complete(struct thr_info *);
+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 request_work(struct thr_info *);
 extern struct work *get_work(struct thr_info *);
 extern enum test_nonce2_result _test_nonce2(struct work *, uint32_t nonce, bool checktarget);

+ 6 - 0
util.h

@@ -97,6 +97,12 @@ static inline void align_len(size_t *len)
 	timer_set_delay(tvp_timer, &tv_now, usecs);  \
 } while(0)
 
+static inline
+bool timer_passed(struct timeval *tvp_timer, struct timeval *tvp_now)
+{
+	return (tvp_timer->tv_sec != -1 && timercmp(tvp_timer, tvp_now, <));
+}
+
 static inline
 void reduce_timeout_to(struct timeval *tvp_timeout, struct timeval *tvp_time)
 {