Browse Source

Merge commit 'ab764da' into bfgminer

Luke Dashjr 14 years ago
parent
commit
3d6b95f519
7 changed files with 324 additions and 173 deletions
  1. BIN
      bitstreams/ztex_ufm1_15d4.bit
  2. BIN
      bitstreams/ztex_ufm1_15y1.bit
  3. 58 28
      driver-ztex.c
  4. 114 29
      libztex.c
  5. 9 2
      libztex.h
  6. 134 114
      miner.c
  7. 9 0
      miner.h

BIN
bitstreams/ztex_ufm1_15d4.bit


BIN
bitstreams/ztex_ufm1_15y1.bit


+ 58 - 28
driver-ztex.c

@@ -87,13 +87,16 @@ static bool ztex_updateFreq(struct libztex_device* ztex)
 		}
 	}
 
-	if (bestM != ztex->freqM) 
+	if (bestM != ztex->freqM) {
+		libztex_selectFpga(ztex, 0);
 		libztex_setFreq(ztex, bestM);
+	}
 
 	maxM = ztex->freqMDefault;
 	while (maxM < ztex->freqMaxM && ztex->errorWeight[maxM + 1] > 100)
 		maxM++;
 	if ((bestM < (1.0 - LIBZTEX_OVERHEATTHRESHOLD) * maxM) && bestM < maxM - 1) {
+		libztex_selectFpga(ztex, 0);
 		libztex_resetFpga(ztex);
 		applog(LOG_ERR, "%s: frequency drop of %.1f%% detect. This may be caused by overheating. FPGA is shut down to prevent damage.",
 		       ztex->repr, (1.0 - 1.0 * bestM / maxM) * 100);
@@ -147,10 +150,11 @@ static uint64_t ztex_scanhash(struct thr_info *thr, struct work *work,
 {
 	struct libztex_device *ztex;
 	unsigned char sendbuf[44];
-	int i, j;
-	uint32_t backlog[GOLDEN_BACKLOG];
-	int backlog_p = 0;
-	uint32_t lastnonce[GOLDEN_BACKLOG], nonce, noncecnt = 0;
+	int i, j, k;
+	uint32_t *backlog;
+	int backlog_p = 0, backlog_max;
+	uint32_t *lastnonce;
+	uint32_t nonce, noncecnt = 0;
 	bool overflow, found, rv;
 	struct libztex_hash_data hdata[GOLDEN_BACKLOG];
 
@@ -158,7 +162,8 @@ static uint64_t ztex_scanhash(struct thr_info *thr, struct work *work,
 
 	memcpy(sendbuf, work->data + 64, 12);
 	memcpy(sendbuf + 12, work->midstate, 32);
-	memset(backlog, 0, sizeof(backlog));
+	
+	libztex_selectFpga(ztex, 0);
 	i = libztex_sendHashData(ztex, sendbuf);
 	if (i < 0) {
 		// Something wrong happened in send
@@ -173,19 +178,33 @@ static uint64_t ztex_scanhash(struct thr_info *thr, struct work *work,
 		}
 	}
 	
-	applog(LOG_DEBUG, "sent hashdata");
-
-	for (i = 0; i < ztex->numNonces; i++)
-		lastnonce[i] = 0;
+	applog(LOG_DEBUG, "%s: sent hashdata", ztex->repr);
 
+	lastnonce = malloc(sizeof(uint32_t)*ztex->numNonces);
+	if (lastnonce == NULL) {
+		applog(LOG_ERR, "%s: failed to allocate lastnonce[%d]", ztex->repr, ztex->numNonces);
+		return 0;
+	}
+	memset(lastnonce, 0, sizeof(uint32_t)*ztex->numNonces);
+	
+	backlog_max = ztex->numNonces * (1 + ztex->extraSolutions);
+	backlog = malloc(sizeof(uint32_t) * backlog_max);
+	if (backlog == NULL) {
+		applog(LOG_ERR, "%s: failed to allocate backlog[%d]", ztex->repr, backlog_max);
+		return 0;
+	}
+	memset(backlog, 0, sizeof(uint32_t) * backlog_max);
+	
 	overflow = false;
 
+	applog(LOG_DEBUG, "%s: entering poll loop", ztex->repr);
 	while (!(overflow || work_restart[thr->id].restart)) {
 		usleep(250000);
 		if (work_restart[thr->id].restart) {
 			applog(LOG_DEBUG, "%s: New work detected", ztex->repr);
 			break;
 		}
+		libztex_selectFpga(ztex, 0);
 		i = libztex_readHashData(ztex, &hdata[0]);
 		if (i < 0) {
 			// Something wrong happened in read
@@ -196,6 +215,8 @@ static uint64_t ztex_scanhash(struct thr_info *thr, struct work *work,
 				// And there's nothing we can do about it
 				ztex_disable(thr);
 				applog(LOG_ERR, "%s: Failed to read hash data with err %d, giving up", ztex->repr, i);
+				free(lastnonce);
+				free(backlog);
 				return 0;
 			}
 		}
@@ -227,26 +248,28 @@ static uint64_t ztex_scanhash(struct thr_info *thr, struct work *work,
 				thr->cgpu->hw_errors++;
 				continue;
 			}
-			nonce = hdata[i].goldenNonce;
-			if (nonce > 0) {
-				found = false;
-				for (j = 0; j < GOLDEN_BACKLOG; j++) {
-					if (backlog[j] == nonce) {
-						found = true;
-						break;
+			for (j=0; j<=ztex->extraSolutions; j++) {
+				nonce = hdata[i].goldenNonce[j];
+				if (nonce > 0) {
+					found = false;
+					for (k = 0; k < backlog_max; k++) {
+						if (backlog[k] == nonce) {
+							found = true;
+							break;
+						}
 					}
-				}
-				if (!found) {
-					applog(LOG_DEBUG, "%s: Share found", ztex->repr);
-					backlog[backlog_p++] = nonce;
-					if (backlog_p >= GOLDEN_BACKLOG)
-						backlog_p = 0;
+					if (!found) {
+						applog(LOG_DEBUG, "%s: Share found N%dE%d", ztex->repr, i, j);
+						backlog[backlog_p++] = nonce;
+						if (backlog_p >= backlog_max)
+							backlog_p = 0;
 #if defined(__BIGENDIAN__) || defined(MIPSEB)
-					nonce = swab32(nonce);
+						nonce = swab32(nonce);
 #endif
-					work->blk.nonce = 0xffffffff;
-					rv = submit_nonce(thr, work, nonce);
-					applog(LOG_DEBUG, "%s: submitted %0.8x %d", ztex->repr, nonce, rv);
+						work->blk.nonce = 0xffffffff;
+						rv = submit_nonce(thr, work, nonce);
+						applog(LOG_DEBUG, "%s: submitted %0.8x %d", ztex->repr, nonce, rv);
+					}
 				}
 			}
 
@@ -258,14 +281,21 @@ static uint64_t ztex_scanhash(struct thr_info *thr, struct work *work,
 	if (ztex->errorRate[ztex->freqM] > ztex->maxErrorRate[ztex->freqM])
 		ztex->maxErrorRate[ztex->freqM] = ztex->errorRate[ztex->freqM];
 
-	if (!ztex_updateFreq(ztex))
+	if (!ztex_updateFreq(ztex)) {
 		// Something really serious happened, so mark this thread as dead!
+		free(lastnonce);
+		free(backlog);
+		
 		return 0;
+	}
 
 	applog(LOG_DEBUG, "%s: exit %1.8X", ztex->repr, noncecnt);
 
 	work->blk.nonce = 0xffffffff;
 
+	free(lastnonce);
+	free(backlog);
+	
 	return noncecnt > 0? noncecnt: 1;
 }
 

+ 114 - 29
libztex.c

@@ -41,7 +41,8 @@
 #define CAPABILITY_HS_FPGA 0,5
 //* Capability index for AVR XMEGA support.
 #define CAPABILITY_MAC_EEPROM 0,6
-
+//* Capability index for multi FPGA support.
+#define CAPABILITY_MULTI_FPGA 0,7
 
 
 static bool libztex_checkDevice(struct libusb_device *dev)
@@ -65,7 +66,7 @@ static bool libztex_checkCapability(struct libztex_device *ztex, int i, int j)
 {
 	if (!((i >= 0) && (i <= 5) && (j >= 0) && (j < 8) &&
 	     (((ztex->interfaceCapabilities[i] & 255) & (1 << j)) != 0))) {
-		applog(LOG_ERR, "%s: capability missing: %d %d", ztex->repr, i, i);
+		applog(LOG_ERR, "%s: capability missing: %d %d", ztex->repr, i, j);
 		return false;
 	}
 	return true;
@@ -223,9 +224,48 @@ int libztex_configureFpga(struct libztex_device *ztex)
 	return libztex_configureFpgaLS(ztex, buf, true, 2);
 }
 
-int libztex_setFreq(struct libztex_device *ztex, uint16_t freq)
-{
+int libztex_numberOfFpgas(struct libztex_device *ztex) {
 	int cnt;
+	unsigned char buf[3];
+	if (ztex->numberOfFpgas < 0) {
+		if (libztex_checkCapability(ztex, CAPABILITY_MULTI_FPGA)) {
+			cnt = libusb_control_transfer(ztex->hndl, 0xc0, 0x50, 0, 0, buf, 3, 1000);
+			if (unlikely(cnt < 0)) {
+				applog(LOG_ERR, "%s: Failed getMultiFpgaInfo with err %d", ztex->repr, cnt);
+				return cnt;
+			}
+			ztex->numberOfFpgas = buf[0] + 1;
+			ztex->selectedFpga = buf[1];
+			ztex->parallelConfigSupport = (buf[2] == 1);
+		} else {
+			ztex->numberOfFpgas = 1;
+			ztex->selectedFpga = 0;
+			ztex->parallelConfigSupport = false;
+		}
+	}
+	return ztex->numberOfFpgas;
+}
+
+int libztex_selectFpga(struct libztex_device *ztex, int number) {
+	int cnt, fpgacnt = libztex_numberOfFpgas(ztex);
+	if (number < 0 || number >= fpgacnt) {
+		applog(LOG_WARNING, "%s: Trying to select wrong fpga (%d in %d)", ztex->repr, number, fpgacnt);
+		return 1;
+	}
+	if (ztex->selectedFpga != number && libztex_checkCapability(ztex, CAPABILITY_MULTI_FPGA)) {
+		cnt = libusb_control_transfer(ztex->hndl, 0x40, 0x51, number, 0, NULL, 0, 500);
+		if (unlikely(cnt < 0)) {
+			applog(LOG_ERR, "Ztex check device: Failed to set fpga with err %d", cnt);
+			return cnt;
+		}
+		ztex->selectedFpga = number;
+	}
+	return 0;
+}
+
+int libztex_setFreq(struct libztex_device *ztex, uint16_t freq) {
+	int cnt;
+	uint16_t oldfreq = ztex->freqM;
 
 	if (freq > ztex->freqMaxM)
 		freq = ztex->freqMaxM;
@@ -236,7 +276,8 @@ int libztex_setFreq(struct libztex_device *ztex, uint16_t freq)
 		return cnt;
 	}
 	ztex->freqM = freq;
-	applog(LOG_WARNING, "%s: Frequency change to %0.2f Mhz", ztex->repr, ztex->freqM1 * (ztex->freqM + 1));
+	applog(LOG_WARNING, "%s: Frequency change from %0.2f to %0.2f Mhz",
+	       ztex->repr, ztex->freqM1 * (oldfreq + 1), ztex->freqM1 * (ztex->freqM + 1));
 
 	return 0;
 }
@@ -246,14 +287,22 @@ int libztex_resetFpga(struct libztex_device *ztex)
 	return libusb_control_transfer(ztex->hndl, 0x40, 0x31, 0, 0, NULL, 0, 1000);
 }
 
-int libztex_prepare_device(struct libusb_device *dev, struct libztex_device** ztex)
-{
+int libztex_suspend(struct libztex_device *ztex) {
+	if (ztex->suspendSupported) {
+		return libusb_control_transfer(ztex->hndl, 0x40, 0x84, 0, 0, NULL, 0, 1000);
+	} else {
+		return 0;
+	}
+}
+
+int libztex_prepare_device(struct libusb_device *dev, struct libztex_device** ztex) {
 	struct libztex_device *newdev;
+	int i, cnt, err;
 	unsigned char buf[64];
-	int cnt, err;
 
 	newdev = malloc(sizeof(struct libztex_device));
 	newdev->bitFileName = NULL;
+	newdev->numberOfFpgas = -1;
 	newdev->valid = false;
 	newdev->hndl = NULL;
 	*ztex = newdev;
@@ -266,7 +315,7 @@ int libztex_prepare_device(struct libusb_device *dev, struct libztex_device** zt
 
 	// Check vendorId and productId
 	if (!(newdev->descriptor.idVendor == LIBZTEX_IDVENDOR &&
-				newdev->descriptor.idProduct == LIBZTEX_IDPRODUCT)) {
+	    newdev->descriptor.idProduct == LIBZTEX_IDPRODUCT)) {
 		applog(LOG_ERR, "Not a ztex device? %0.4X, %0.4X", newdev->descriptor.idVendor, newdev->descriptor.idProduct);
 		return 1;
 	}
@@ -290,7 +339,7 @@ int libztex_prepare_device(struct libusb_device *dev, struct libztex_device** zt
 		return cnt;
 	}
 	
-	if ( buf[0] != 40 || buf[1] != 1 || buf[2] != 'Z' || buf[3] != 'T' || buf[4] != 'E' || buf[5] != 'X' ) {
+	if (buf[0] != 40 || buf[1] != 1 || buf[2] != 'Z' || buf[3] != 'T' || buf[4] != 'E' || buf[5] != 'X') {
 		applog(LOG_ERR, "Ztex check device: Error reading ztex descriptor");
 		return 2;
 	}
@@ -327,20 +376,46 @@ int libztex_prepare_device(struct libusb_device *dev, struct libztex_device** zt
 		return cnt;
 	}
 
-	if (unlikely(buf[0] != 4)) {
-		if (unlikely(buf[0] != 2)) {
+	if (unlikely(buf[0] != 5)) {
+		if (unlikely(buf[0] != 2 && buf[0] != 4)) {
 			applog(LOG_ERR, "Invalid BTCMiner descriptor version. Firmware must be updated (%d).", buf[0]);
 			return 3;
 		}
-		applog(LOG_WARNING, "Firmware out of date");
+		applog(LOG_WARNING, "Firmware out of date (%d).", buf[0]);
+	}
+
+	i = buf[0] > 4? 11: (buf[0] > 2? 10: 8);
+
+	while (cnt < 64 && buf[cnt] != 0)
+		cnt++;
+	if (cnt < i + 1) {
+		applog(LOG_ERR, "Invalid bitstream file name .");
+		return 4;
 	}
 
+	newdev->bitFileName = malloc(sizeof(char) * (cnt + 1));
+	memcpy(newdev->bitFileName, &buf[i], cnt);
+	newdev->bitFileName[cnt] = 0;	
+
 	newdev->numNonces = buf[1] + 1;
 	newdev->offsNonces = ((buf[2] & 255) | ((buf[3] & 255) << 8)) - 10000;
 	newdev->freqM1 = ((buf[4] & 255) | ((buf[5] & 255) << 8) ) * 0.01;
 	newdev->freqMaxM = (buf[7] & 255);
 	newdev->freqM = (buf[6] & 255);
 	newdev->freqMDefault = newdev->freqM;
+	newdev->suspendSupported = (buf[0] == 5);
+	newdev->hashesPerClock = buf[0] > 2? (((buf[8] & 255) | ((buf[9] & 255) << 8)) + 1) / 128.0: 1.0;
+	newdev->extraSolutions = buf[0] > 4? buf[10]: 0;
+	
+	applog(LOG_DEBUG, "PID: %d numNonces: %d offsNonces: %d freqM1: %f freqMaxM: %d freqM: %d suspendSupported: %s hashesPerClock: %f extraSolutions: %d",
+	                 buf[0], newdev->numNonces, newdev->offsNonces, newdev->freqM1, newdev->freqMaxM, newdev->freqM, newdev->suspendSupported ? "T": "F", 
+	                 newdev->hashesPerClock, newdev->extraSolutions);
+
+	if (buf[0] < 4) {
+		if (strncmp(newdev->bitFileName, "ztex_ufm1_15b", 13) != 0)
+			newdev->hashesPerClock = 0.5;
+		applog(LOG_WARNING, "HASHES_PER_CLOCK not defined, assuming %0.2f", newdev->hashesPerClock);
+	}
 
 	for (cnt=0; cnt < 255; cnt++) {
 		newdev->errorCount[cnt] = 0;
@@ -349,10 +424,6 @@ int libztex_prepare_device(struct libusb_device *dev, struct libztex_device** zt
 		newdev->maxErrorRate[cnt] = 0;
 	}
 
-	cnt = strlen((char *)&buf[buf[0] == 4? 10: 8]);
-	newdev->bitFileName = malloc(sizeof(char) * (cnt + 1));
-	memcpy(newdev->bitFileName, &buf[buf[0] == 4? 10: 8], cnt + 1);
-
 	newdev->usbbus = libusb_get_bus_number(dev);
 	newdev->usbaddress = libusb_get_device_address(dev);
 	sprintf(newdev->repr, "ZTEX %.3d:%.3d-%s", newdev->usbbus, newdev->usbaddress, newdev->snString);
@@ -437,29 +508,43 @@ int libztex_sendHashData(struct libztex_device *ztex, unsigned char *sendbuf)
 	return cnt;
 }
 
-int libztex_readHashData(struct libztex_device *ztex, struct libztex_hash_data nonces[])
-{
-	// length of buf must be 8 * (numNonces + 1)
-	unsigned char rbuf[12 * 8];
-	int cnt, i;
+int libztex_readHashData(struct libztex_device *ztex, struct libztex_hash_data nonces[]) {
+	int bufsize = 12 + ztex->extraSolutions * 4;
+	unsigned char *rbuf;
+	int cnt, i, j;
 
 	if (ztex->hndl == NULL)
 		return 0;
 	
-	cnt = libusb_control_transfer(ztex->hndl, 0xc0, 0x81, 0, 0, rbuf, 12 * ztex->numNonces, 1000);
+	rbuf = malloc(sizeof(unsigned char) * (ztex->numNonces * bufsize));
+	if (rbuf == NULL) {
+		applog(LOG_ERR, "%s: Failed to allocate memory for reading nonces", ztex->repr);
+		return 0;
+	}
+	cnt = libusb_control_transfer(ztex->hndl, 0xc0, 0x81, 0, 0, rbuf, bufsize * ztex->numNonces, 1000);
 	if (unlikely(cnt < 0)) {
 		applog(LOG_ERR, "%s: Failed readHashData with err %d", ztex->repr, cnt);
+		free(rbuf);
 		return cnt;
 	}
 
-	for (i = 0; i < ztex->numNonces; i++) {
-		memcpy((char*)&nonces[i].goldenNonce, &rbuf[i * 12], 4);
-		nonces[i].goldenNonce -= ztex->offsNonces;
-		memcpy((char*)&nonces[i].nonce, &rbuf[(i * 12) + 4], 4);
+	for (i=0; i<ztex->numNonces; i++) {
+		memcpy((char*)&nonces[i].goldenNonce[0], &rbuf[i*bufsize], 4);
+		nonces[i].goldenNonce[0] -= ztex->offsNonces;
+		//applog(LOG_DEBUG, "W %d:0 %0.8x", i, nonces[i].goldenNonce[0]);
+		
+		memcpy((char*)&nonces[i].nonce, &rbuf[(i*bufsize)+4], 4);
 		nonces[i].nonce -= ztex->offsNonces;
-		memcpy((char*)&nonces[i].hash7, &rbuf[(i * 12) + 8], 4);
+		memcpy((char*)&nonces[i].hash7, &rbuf[(i*bufsize)+8], 4);
+
+		for (j=0; j<ztex->extraSolutions; j++) {
+			memcpy((char*)&nonces[i].goldenNonce[j+1], &rbuf[(i*bufsize)+12+(j*4)], 4);
+			nonces[i].goldenNonce[j+1] -= ztex->offsNonces;
+			//applog(LOG_DEBUG, "W %d:%d %0.8x", i, j+1, nonces[i].goldenNonce[j+1]);
+		}
 	}
-	
+
+	free(rbuf);
 	return cnt;
 }
 

+ 9 - 2
libztex.h

@@ -32,7 +32,7 @@
 
 #define LIBZTEX_MAXMAXERRORRATE 0.05
 #define LIBZTEX_ERRORHYSTERESIS 0.1
-#define LIBZTEX_OVERHEATTHRESHOLD 0.5
+#define LIBZTEX_OVERHEATTHRESHOLD 0.4
 
 struct libztex_fpgastate {
 	bool fpgaConfigured;
@@ -62,12 +62,19 @@ struct libztex_device {
 	uint8_t freqMaxM;
 	uint8_t freqMDefault;
 	char* bitFileName;
+	bool suspendSupported;
+	double hashesPerClock;
+	uint8_t extraSolutions;
 
 	double errorCount[256];
 	double errorWeight[256];
 	double errorRate[256];
 	double maxErrorRate[256];
 
+	int numberOfFpgas;
+	int selectedFpga;
+	bool parallelConfigSupport;
+	
 	char repr[64];
 };
 
@@ -77,7 +84,7 @@ struct libztex_dev_list {
 };
 
 struct libztex_hash_data {
-	uint32_t goldenNonce;
+	uint32_t goldenNonce[2];
 	uint32_t nonce;
 	uint32_t hash7;
 };

+ 134 - 114
miner.c

@@ -378,6 +378,9 @@ sharelog(const char*disposition, const struct work*work) {
 		applog(LOG_ERR, "sharelog fwrite error");
 }
 
+static void *submit_work_thread(void *userdata);
+static void *get_work_thread(void *userdata);
+
 static void add_pool(void)
 {
 	struct pool *pool;
@@ -395,6 +398,11 @@ static void add_pool(void)
 	}
 	/* Make sure the pool doesn't think we've been idle since time 0 */
 	pool->tv_idle.tv_sec = ~0UL;
+
+	if (unlikely(pthread_create(&pool->submit_thread, NULL, submit_work_thread, (void *)pool)))
+		quit(1, "Failed to create pool submit thread");
+	if (unlikely(pthread_create(&pool->getwork_thread, NULL, get_work_thread, (void *)pool)))
+		quit(1, "Failed to create pool getwork thread");
 }
 
 /* Pool variant of test and set */
@@ -1608,18 +1616,13 @@ static bool submit_upstream_work(const struct work *work)
 	bool rc = false;
 	int thr_id = work->thr_id;
 	struct cgpu_info *cgpu = thr_info[thr_id].cgpu;
-	CURL *curl = curl_easy_init();
 	struct pool *pool = work->pool;
+	CURL *curl = pool->submit_curl;
 	bool rolltime;
 	uint32_t *hash32;
 	char hashshow[64+1] = "";
 	bool isblock;
 
-	if (unlikely(!curl)) {
-		applog(LOG_ERR, "CURL initialisation failed");
-		return rc;
-	}
-
 #ifdef __BIG_ENDIAN__
         int swapcounter = 0;
         for (swapcounter = 0; swapcounter < 32; swapcounter++)
@@ -1753,7 +1756,6 @@ static bool submit_upstream_work(const struct work *work)
 out:
 	free(hexstr);
 out_nofree:
-	curl_easy_cleanup(curl);
 	return rc;
 }
 
@@ -1797,41 +1799,26 @@ static void get_benchmark_work(struct work *work)
 	memcpy(work, &bench_block, min_size);
 }
 
-static bool get_upstream_work(struct work *work, bool lagging)
+static bool get_upstream_work(struct work *work)
 {
-	bool rc = false, req_longpoll = false;
-	struct pool *pool;
+	struct pool *pool = work->pool;
+	CURL *curl = pool->getwork_curl;
 	json_t *val = NULL;
+	bool rc = false;
 	int retries = 0;
-	CURL *curl;
 	char *url;
 
-	curl = curl_easy_init();
-	if (unlikely(!curl)) {
-		applog(LOG_ERR, "CURL initialisation failed");
-		return rc;
-	}
-
-	pool = select_pool(lagging);
 	applog(LOG_DEBUG, "DBG: sending %s get RPC call: %s", pool->rpc_url, rpc_req);
 
 	url = pool->rpc_url;
 
-	/* If this is the current pool and supports longpoll but has not sent
-	 * a longpoll, send one now */
-	if (unlikely(want_longpoll && !pool->is_lp && pool == current_pool() &&
-		pool->hdr_path && !pool_tset(pool, &pool->lp_sent))) {
-			req_longpoll = true;
-			url = pool->lp_url;
-	}
-
 retry:
 	/* A single failure response here might be reported as a dead pool and
 	 * there may be temporary denied messages etc. falsely reporting
 	 * failure so retry a few times before giving up */
 	while (!val && retries++ < 3) {
 		val = json_rpc_call(curl, url, pool->rpc_userpass, rpc_req,
-			    false, req_longpoll, &work->rolltime, pool, false);
+			    false, false, &work->rolltime, pool, false);
 	}
 	if (unlikely(!val)) {
 		applog(LOG_DEBUG, "Failed json_rpc_call in get_upstream_work");
@@ -1846,13 +1833,12 @@ retry:
 		goto retry;
 	}
 	work->pool = pool;
-	work->longpoll = req_longpoll;
+	work->longpoll = false;
 	total_getworks++;
 	pool->getwork_requested++;
 
 	json_decref(val);
 out:
-	curl_easy_cleanup(curl);
 
 	return rc;
 }
@@ -2013,60 +1999,85 @@ static void sighandler(int __maybe_unused sig)
 	kill_work();
 }
 
+static void start_longpoll(void);
+static void stop_longpoll(void);
+
+/* One get work thread is created per pool, so as to use one curl handle for
+ * all getwork reqeusts from the same pool, minimising connections opened, but
+ * separate from the submit work curl handle to not delay share submissions due
+ * to getwork traffic */
 static void *get_work_thread(void *userdata)
 {
-	struct workio_cmd *wc = (struct workio_cmd *)userdata;
-	struct work *ret_work;
-	int failures = 0;
+	struct pool *pool = (struct pool *)userdata;
+	struct workio_cmd *wc;
 
 	pthread_detach(pthread_self());
-	ret_work = make_work();
 
-	if (wc->thr)
-		ret_work->thr = wc->thr;
-	else
-		ret_work->thr = NULL;
+	/* getwork_q memory never freed */
+	pool->getwork_q = tq_new();
+	if (!pool->getwork_q)
+		quit(1, "Failed to tq_new in get_work_thread");
 
-	/* obtain new work from bitcoin via JSON-RPC */
-	while (!get_upstream_work(ret_work, wc->lagging)) {
-		if (unlikely((opt_retries >= 0) && (++failures > opt_retries))) {
-			applog(LOG_ERR, "json_rpc_call failed, terminating workio thread");
-			free_work(ret_work);
-			kill_work();
-			goto out;
+	/* getwork_curl never cleared */
+	pool->getwork_curl = curl_easy_init();
+	if (unlikely(!pool->getwork_curl))
+		quit(1, "Failed to initialise pool getwork CURL");
+
+	while ((wc = tq_pop(pool->getwork_q, NULL)) != NULL) {
+		struct work *ret_work;
+		int failures = 0;
+
+		if (unlikely(want_longpoll && !pool->is_lp && pool == current_pool() &&
+			pool->hdr_path && !pool_tset(pool, &pool->lp_sent))) {
+				stop_longpoll();
+				start_longpoll();
 		}
 
-		/* pause, then restart work-request loop */
-		applog(LOG_DEBUG, "json_rpc_call failed on get work, retry after %d seconds",
-			fail_pause);
-		sleep(fail_pause);
-		fail_pause += opt_fail_pause;
-	}
-	fail_pause = opt_fail_pause;
+		ret_work = make_work();
+
+		if (wc->thr)
+			ret_work->thr = wc->thr;
+		else
+			ret_work->thr = NULL;
+
+		ret_work->pool = pool;
+
+		/* obtain new work from bitcoin via JSON-RPC */
+		while (!get_upstream_work(ret_work)) {
+			if (unlikely((opt_retries >= 0) && (++failures > opt_retries))) {
+				applog(LOG_ERR, "json_rpc_call failed, terminating workio thread");
+				free_work(ret_work);
+				kill_work();
+				break;
+			}
+
+			/* pause, then restart work-request loop */
+			applog(LOG_DEBUG, "json_rpc_call failed on get work, retry after %d seconds",
+				fail_pause);
+			sleep(fail_pause);
+			fail_pause += opt_fail_pause;
+		}
+		fail_pause = opt_fail_pause;
 
-	applog(LOG_DEBUG, "Pushing work to requesting thread");
+		applog(LOG_DEBUG, "Pushing work to requesting thread");
 
-	/* send work to requesting thread */
-	if (unlikely(!tq_push(thr_info[stage_thr_id].q, ret_work))) {
-		applog(LOG_ERR, "Failed to tq_push work in workio_get_work");
-		kill_work();
-		free_work(ret_work);
+		/* send work to requesting thread */
+		if (unlikely(!tq_push(thr_info[stage_thr_id].q, ret_work))) {
+			applog(LOG_ERR, "Failed to tq_push work in workio_get_work");
+			kill_work();
+			free_work(ret_work);
+		}
+		workio_cmd_free(wc);
 	}
 
-out:
-	workio_cmd_free(wc);
 	return NULL;
 }
 
 static bool workio_get_work(struct workio_cmd *wc)
 {
-	pthread_t get_thread;
+	struct pool *pool = select_pool(wc->lagging);
 
-	if (unlikely(pthread_create(&get_thread, NULL, get_work_thread, (void *)wc))) {
-		applog(LOG_ERR, "Failed to create get_work_thread");
-		return false;
-	}
-	return true;
+	return tq_push(pool->getwork_q, wc);
 }
 
 static bool stale_work(struct work *work, bool share)
@@ -2092,64 +2103,76 @@ static bool stale_work(struct work *work, bool share)
 	return false;
 }
 
+/* One submit work thread is created per pool, so as to use one curl handle
+ * for all submissions to the same pool, minimising connections opened, but
+ * separate from the getwork curl handle to not delay share submission due to
+ * getwork traffic */
 static void *submit_work_thread(void *userdata)
 {
-	struct workio_cmd *wc = (struct workio_cmd *)userdata;
-	struct work *work = wc->u.work;
-	struct pool *pool = work->pool;
-	int failures = 0;
+	struct pool *pool = (struct pool *)userdata;
+	struct workio_cmd *wc;
 
 	pthread_detach(pthread_self());
 
-	if (stale_work(work, true)) {
-		if (opt_submit_stale)
-			applog(LOG_NOTICE, "Stale share detected, submitting as user requested");
-		else if (pool->submit_old)
-			applog(LOG_NOTICE, "Stale share detected, submitting as pool requested");
-		else {
-			applog(LOG_NOTICE, "Stale share detected, discarding");
-			sharelog("discard", work);
-			total_stale++;
-			pool->stale_shares++;
-			goto out;
+	/* submit_q memory never freed */
+	pool->submit_q = tq_new();
+	if (!pool->submit_q )
+		quit(1, "Failed to tq_new in submit_work_thread");
+
+	/* submit_curl never cleared */
+	pool->submit_curl = curl_easy_init();
+	if (unlikely(!pool->submit_curl))
+		quit(1, "Failed to initialise pool submit CURL");
+
+	while ((wc = tq_pop(pool->submit_q, NULL)) != NULL) {
+		struct work *work = wc->u.work;
+		int failures = 0;
+
+		if (stale_work(work, true)) {
+			if (opt_submit_stale)
+				applog(LOG_NOTICE, "Stale share detected, submitting as user requested");
+			else if (pool->submit_old)
+				applog(LOG_NOTICE, "Stale share detected, submitting as pool requested");
+			else {
+				applog(LOG_NOTICE, "Stale share detected, discarding");
+				sharelog("discard", work);
+				total_stale++;
+				pool->stale_shares++;
+				workio_cmd_free(wc);
+				continue;
+			}
 		}
-	}
 
-	/* submit solution to bitcoin via JSON-RPC */
-	while (!submit_upstream_work(work)) {
-		if (!opt_submit_stale && stale_work(work, true)) {
-			applog(LOG_NOTICE, "Stale share detected, discarding");
-			total_stale++;
-			pool->stale_shares++;
-			break;
-		}
-		if (unlikely((opt_retries >= 0) && (++failures > opt_retries))) {
-			applog(LOG_ERR, "Failed %d retries ...terminating workio thread", opt_retries);
-			kill_work();
-			break;
-		}
+		/* submit solution to bitcoin via JSON-RPC */
+		while (!submit_upstream_work(work)) {
+			if (!opt_submit_stale && stale_work(work, true)) {
+				applog(LOG_NOTICE, "Stale share detected, discarding");
+				total_stale++;
+				pool->stale_shares++;
+				break;
+			}
+			if (unlikely((opt_retries >= 0) && (++failures > opt_retries))) {
+				applog(LOG_ERR, "Failed %d retries ...terminating workio thread", opt_retries);
+				kill_work();
+				break;
+			}
 
-		/* pause, then restart work-request loop */
-		applog(LOG_INFO, "json_rpc_call failed on submit_work, retry after %d seconds",
-			fail_pause);
-		sleep(fail_pause);
-		fail_pause += opt_fail_pause;
+			/* pause, then restart work-request loop */
+			applog(LOG_INFO, "json_rpc_call failed on submit_work, retry after %d seconds",
+				fail_pause);
+			sleep(fail_pause);
+			fail_pause += opt_fail_pause;
+		}
+		fail_pause = opt_fail_pause;
+		workio_cmd_free(wc);
 	}
-	fail_pause = opt_fail_pause;
-out:
-	workio_cmd_free(wc);
+
 	return NULL;
 }
 
 static bool workio_submit_work(struct workio_cmd *wc)
 {
-	pthread_t submit_thread;
-
-	if (unlikely(pthread_create(&submit_thread, NULL, submit_work_thread, (void *)wc))) {
-		applog(LOG_ERR, "Failed to create submit_work_thread");
-		return false;
-	}
-	return true;
+	return tq_push(wc->u.work->pool->submit_q, wc);
 }
 
 /* Find the pool that currently has the highest priority */
@@ -2916,9 +2939,6 @@ retry:
 }
 #endif
 
-static void start_longpoll(void);
-static void stop_longpoll(void);
-
 #ifdef HAVE_CURSES
 static void set_options(void)
 {
@@ -3892,7 +3912,6 @@ static void *longpoll_thread(void *userdata)
 	bool rolltime;
 
 	pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
-	pthread_detach(pthread_self());
 
 	curl = curl_easy_init();
 	if (unlikely(!curl)) {
@@ -3971,12 +3990,13 @@ out:
 	return NULL;
 }
 
-__maybe_unused
 static void stop_longpoll(void)
 {
 	struct thr_info *thr = &thr_info[longpoll_thr_id];
 
 	thr_info_cancel(thr);
+	if (have_longpoll)
+		pthread_join(thr->pth, NULL);
 	have_longpoll = false;
 	tq_freeze(thr->q);
 }

+ 9 - 0
miner.h

@@ -625,6 +625,15 @@ struct pool {
 	char *rpc_user, *rpc_pass;
 
 	pthread_mutex_t pool_lock;
+
+	struct thread_q *submit_q;
+	struct thread_q *getwork_q;
+
+	pthread_t submit_thread;
+	pthread_t getwork_thread;
+
+	CURL *submit_curl;
+	CURL *getwork_curl;
 };
 
 struct work {