Commit ecfd2f4d9cb1fc8c024a7e0f286470f12833f894

Vitalii Demianets 2013-12-17T15:41:19

knc: Make per-core statistics available through API

diff --git a/driver-knc-spi-fpga.c b/driver-knc-spi-fpga.c
index a444c4d..b892c09 100644
--- a/driver-knc-spi-fpga.c
+++ b/driver-knc-spi-fpga.c
@@ -32,6 +32,7 @@
 #define SPI_DELAY_USECS		0
 /* Max number of ASICs permitted on one SPI device */
 #define MAX_ASICS		6
+#define CORES_PER_ASIC	192
 
 /* How many hardware errors in a row before disabling the core */
 #define HW_ERR_LIMIT		10
@@ -44,8 +45,10 @@
 
 #define WORK_STALE_US		60000000
 
+#define	SECONDS_IN_MINUTE	60
+
 /* Keep core disabled for no longer than 15 minutes */
-#define CORE_DISA_PERIOD_US	(15 * 60 * 1000000)
+#define CORE_DISA_PERIOD_US	(15 * SECONDS_IN_MINUTE * 1000000)
 
 /* DP = Disable Policy */
 bool opt_knc_DP_checkworkid = false;
@@ -170,6 +173,13 @@ struct knc_state {
 #define KNC_DISA_CORES_SIZE	(MAX_ASICS * 256)
 	struct core_disa_data disa_cores_fifo[KNC_DISA_CORES_SIZE];
 
+	/* Local stats */
+#define	KNC_MINUTES_IN_STATS_BUFFER	60
+	unsigned int last_hour_shares[MAX_ASICS][256][KNC_MINUTES_IN_STATS_BUFFER];
+	unsigned int last_hour_hwerrs[MAX_ASICS][256][KNC_MINUTES_IN_STATS_BUFFER];
+	unsigned int last_hour_shares_index[MAX_ASICS][256];
+	unsigned int last_hour_hwerrs_index[MAX_ASICS][256];
+
 	pthread_mutex_t lock;
 };
 
@@ -306,6 +316,103 @@ static int spi_transfer(struct spidev_context *ctx, uint8_t *txbuf,
 	return ret;
 }
 
+static void stats_update(unsigned int *data, unsigned int *index, unsigned int cur_index)
+{
+	if (cur_index != *index) {
+		unsigned int i;
+		for (i = (*index + 1) % KNC_MINUTES_IN_STATS_BUFFER; i != cur_index; i = ((i + 1 ) % KNC_MINUTES_IN_STATS_BUFFER))
+			data[i] = 0;
+		data[cur_index] = 0;
+		*index = cur_index;
+	}
+
+	++(data[cur_index]);
+}
+
+static unsigned int get_accumulated_stats(unsigned int *data)
+{
+	int i;
+	unsigned int res = 0;
+	for (i = 0; i < KNC_MINUTES_IN_STATS_BUFFER; ++i)
+		res += data[i];
+
+	return res;
+}
+
+static inline void stats_good_share(struct knc_state *knc, uint32_t asic, uint32_t core, struct timespec *ts)
+{
+	if ((asic >= MAX_ASICS) || (core >= 256))
+		return;
+	unsigned int cur_minute = (ts->tv_sec / SECONDS_IN_MINUTE) % KNC_MINUTES_IN_STATS_BUFFER;
+	stats_update(knc->last_hour_shares[asic][core], &(knc->last_hour_shares_index[asic][core]), cur_minute);
+}
+
+static inline void stats_bad_share(struct knc_state *knc, uint32_t asic, uint32_t core, struct timespec *ts)
+{
+	if ((asic >= MAX_ASICS) || (core >= 256))
+		return;
+	unsigned int cur_minute = (ts->tv_sec / SECONDS_IN_MINUTE) % KNC_MINUTES_IN_STATS_BUFFER;
+	stats_update(knc->last_hour_hwerrs[asic][core], &(knc->last_hour_hwerrs_index[asic][core]), cur_minute);
+}
+
+static inline unsigned int get_hour_shares(struct knc_state *knc, uint32_t asic, uint32_t core)
+{
+	if ((asic >= MAX_ASICS) || (core >= 256))
+		return 0;
+	return get_accumulated_stats(knc->last_hour_shares[asic][core]);
+}
+
+static inline unsigned int get_hour_errors(struct knc_state *knc, uint32_t asic, uint32_t core)
+{
+	if ((asic >= MAX_ASICS) || (core >= 256))
+		return 0;
+	return get_accumulated_stats(knc->last_hour_hwerrs[asic][core]);
+}
+
+static struct api_data *knc_api_stats(struct cgpu_info *cgpu)
+{
+	struct knc_state *knc = cgpu->device_data;
+	struct api_data *root = NULL;
+	char buf[4096];
+	int asic, core;
+	int cursize, n;
+
+	for (asic = 0; asic < MAX_ASICS; ++asic) {
+		char asic_name[128];
+		snprintf(asic_name, sizeof(asic_name), "asic_%d_shares", asic + 1);
+		cursize = 0;
+		for (core = 0; core < CORES_PER_ASIC; ++core) {
+			unsigned int shares = get_hour_shares(knc, asic, core);
+			n = snprintf(buf + cursize, sizeof(buf) - cursize, "%d,", shares);
+			cursize += n;
+			if (sizeof(buf) < cursize) {
+				cursize = sizeof(buf);
+				break;
+			}
+		}
+		if (0 < cursize)
+			buf[cursize - 1] = '\0'; /* last comma */
+		root = api_add_string(root, asic_name, buf, true);
+
+		snprintf(asic_name, sizeof(asic_name), "asic_%d_hwerrs", asic + 1);
+		cursize = 0;
+		for (core = 0; core < CORES_PER_ASIC; ++core) {
+			unsigned int errors = get_hour_errors(knc, asic, core);
+			n = snprintf(buf + cursize, sizeof(buf) - cursize, "%d,", errors);
+			cursize += n;
+			if (sizeof(buf) < cursize) {
+				cursize = sizeof(buf);
+				break;
+			}
+		}
+		if (0 < cursize)
+			buf[cursize - 1] = '\0'; /* last comma */
+		root = api_add_string(root, asic_name, buf, true);
+	}
+
+	return root;
+}
+
 static void disable_core(uint8_t asic, uint8_t core)
 {
 	char str[256];
@@ -391,6 +498,7 @@ static int64_t knc_process_response(struct thr_info *thr, struct cgpu_info *cgpu
 	int submitted, completed, i, num_sent;
 	int next_read_q, next_read_a;
 	struct timeval now;
+	struct timespec ts_now;
 	struct work *work;
 	int64_t us;
 
@@ -409,6 +517,7 @@ static int64_t knc_process_response(struct thr_info *thr, struct cgpu_info *cgpu
 	}
 	/* move works_accepted number of items from queued_fifo to active_fifo */
 	gettimeofday(&now, NULL);
+	clock_gettime(CLOCK_MONOTONIC, &ts_now);
 	submitted = 0;
 
 	for (i = 0; i < rxbuf->works_accepted; ++i) {
@@ -492,12 +601,14 @@ static int64_t knc_process_response(struct thr_info *thr, struct cgpu_info *cgpu
 
 				if (submit_nonce(thr, work,
 						 rxbuf->responses[i].nonce)) {
+					stats_good_share(knc, rxbuf->responses[i].asic, rxbuf->responses[i].core, &ts_now);
 					if (cidx < (int)sizeof(knc->hwerrs)) {
 						knc->hwerrs[cidx] = 0;
 						knc->disa_cnt[cidx] = 0;
 						knc->hwerr_work_id[cidx] = 0xFFFFFFFF;
 					}
 				} else  {
+					stats_bad_share(knc, rxbuf->responses[i].asic, rxbuf->responses[i].core, &ts_now);
 					bool process_hwerr = (cidx < (int)sizeof(knc->hwerrs));
 					if (process_hwerr && opt_knc_DP_checkworkid &&
 					    (knc->hwerr_work_id[cidx] == rxbuf->responses[i].work_id))
@@ -767,4 +878,6 @@ struct device_drv knc_drv = {
 	.scanwork = knc_scanwork,
 	.queue_full = knc_queue_full,
 	.flush_work = knc_flush_work,
+
+	.get_api_stats = knc_api_stats,
 };