Commit 7d35c6921ea1456f0e8e6f560aac41d0ac24ccd1

Kano 2013-06-10T18:52:18

ubsutils lock all access to nodev and cgusb

diff --git a/driver-modminer.c b/driver-modminer.c
index 1519bc9..581016e 100644
--- a/driver-modminer.c
+++ b/driver-modminer.c
@@ -205,7 +205,7 @@ static bool modminer_detect_one(struct libusb_device *dev, struct usb_find_devic
 		tmp->modminer_mutex = modminer->modminer_mutex;
 
 		if (!add_cgpu(tmp)) {
-			tmp = usb_free_cgpu(tmp);
+			tmp = usb_free_cgpu_devlock(tmp, !added);
 			goto unshin;
 		}
 
@@ -228,7 +228,7 @@ shin:
 		modminer->modminer_mutex = NULL;
 	}
 
-	modminer = usb_free_cgpu(modminer);
+	modminer = usb_free_cgpu_devlock(modminer, !added);
 
 	if (added)
 		return true;
diff --git a/usbutils.c b/usbutils.c
index fd3859d..d43e34c 100644
--- a/usbutils.c
+++ b/usbutils.c
@@ -1339,6 +1339,10 @@ void usb_uninit(struct cgpu_info *cgpu)
 	cgpu->usbdev = free_cgusb(cgpu->usbdev);
 }
 
+/*
+ * N.B. this is always called inside
+ *	wr_lock(cgpu->usbinfo.devlock);
+ */
 static void release_cgpu(struct cgpu_info *cgpu)
 {
 	struct cg_usb_device *cgusb = cgpu->usbdev;
@@ -1399,6 +1403,8 @@ struct cgpu_info *usb_copy_cgpu(struct cgpu_info *orig)
 
 	copy->usbinfo.nodev = (copy->usbdev != NULL);
 
+	copy->usbinfo.devlock = orig->usbinfo.devlock;
+
 	return copy;
 }
 
@@ -1415,16 +1421,25 @@ struct cgpu_info *usb_alloc_cgpu(struct device_drv *drv, int threads)
 
 	cgpu->usbinfo.nodev = true;
 
+	cgpu->usbinfo.devlock = calloc(1, sizeof(*(cgpu->usbinfo.devlock)));
+	if (unlikely(!cgpu->usbinfo.devlock))
+		quit(1, "Failed to calloc devlock for %s in usb_alloc_cgpu", drv->dname);
+
+	rwlock_init(cgpu->usbinfo.devlock);
+
 	return cgpu;
 }
 
-struct cgpu_info *usb_free_cgpu(struct cgpu_info *cgpu)
+struct cgpu_info *usb_free_cgpu_devlock(struct cgpu_info *cgpu, bool free_devlock)
 {
 	if (cgpu->drv->copy)
 		free(cgpu->drv);
 
 	free(cgpu->device_path);
 
+	if (free_devlock)
+		free(cgpu->usbinfo.devlock);
+
 	free(cgpu);
 
 	return NULL;
@@ -1446,6 +1461,8 @@ static int _usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct u
 	int err, i, j, k;
 	int bad = USB_INIT_FAIL;
 
+	wr_lock(cgpu->usbinfo.devlock);
+
 	cgpu->usbinfo.bus_number = libusb_get_bus_number(dev);
 	cgpu->usbinfo.device_address = libusb_get_device_address(dev);
 
@@ -1684,6 +1701,8 @@ static int _usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct u
 		cgpu->drv->name = (char *)(found->name);
 	}
 
+	wr_unlock(cgpu->usbinfo.devlock);
+
 	return USB_INIT_OK;
 
 cldame:
@@ -1697,6 +1716,8 @@ dame:
 
 	cgusb = free_cgusb(cgusb);
 
+	wr_unlock(cgpu->usbinfo.devlock);
+
 	return bad;
 }
 
@@ -2145,11 +2166,15 @@ int _usb_read(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *pro
 	unsigned char usbbuf[USB_MAX_READ+4], *ptr;
 	size_t usbbufread;
 
+	wr_lock(cgpu->usbinfo.devlock);
+
 	if (cgpu->usbinfo.nodev) {
 		*buf = '\0';
 		*processed = 0;
 		USB_REJECT(cgpu, MODE_BULK_READ);
 
+		wr_unlock(cgpu->usbinfo.devlock);
+
 		return LIBUSB_ERROR_NO_DEVICE;
 	}
 
@@ -2252,6 +2277,8 @@ int _usb_read(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *pro
 		if (NODEV(err))
 			release_cgpu(cgpu);
 
+		wr_unlock(cgpu->usbinfo.devlock);
+
 		return err;
 	}
 
@@ -2367,6 +2394,8 @@ int _usb_read(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *pro
 	if (NODEV(err))
 		release_cgpu(cgpu);
 
+	wr_unlock(cgpu->usbinfo.devlock);
+
 	return err;
 }
 
@@ -2382,6 +2411,8 @@ int _usb_write(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *pr
 	__maybe_unused bool first = true;
 	int err, sent, tot;
 
+	wr_lock(cgpu->usbinfo.devlock);
+
 	USBDEBUG("USB debug: _usb_write(%s (nodev=%s),ep=%d,buf='%s',bufsiz=%zu,proc=%p,timeout=%u,cmd=%s)", cgpu->drv->name, bool_str(cgpu->usbinfo.nodev), ep, (char *)str_text(buf), bufsiz, processed, timeout, usb_cmdname(cmd));
 
 	*processed = 0;
@@ -2389,6 +2420,8 @@ int _usb_write(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *pr
 	if (cgpu->usbinfo.nodev) {
 		USB_REJECT(cgpu, MODE_BULK_WRITE);
 
+		wr_unlock(cgpu->usbinfo.devlock);
+
 		return LIBUSB_ERROR_NO_DEVICE;
 	}
 
@@ -2441,6 +2474,8 @@ int _usb_write(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *pr
 	if (NODEV(err))
 		release_cgpu(cgpu);
 
+	wr_unlock(cgpu->usbinfo.devlock);
+
 	return err;
 }
 
@@ -2453,11 +2488,15 @@ int _usb_transfer(struct cgpu_info *cgpu, uint8_t request_type, uint8_t bRequest
 	uint32_t *buf = NULL;
 	int err, i, bufsiz;
 
+	wr_lock(cgpu->usbinfo.devlock);
+
 	USBDEBUG("USB debug: _usb_transfer(%s (nodev=%s),type=%"PRIu8",req=%"PRIu8",value=%"PRIu16",index=%"PRIu16",siz=%d,timeout=%u,cmd=%s)", cgpu->drv->name, bool_str(cgpu->usbinfo.nodev), request_type, bRequest, wValue, wIndex, siz, timeout, usb_cmdname(cmd));
 
 	if (cgpu->usbinfo.nodev) {
 		USB_REJECT(cgpu, MODE_CTRL_WRITE);
 
+		wr_unlock(cgpu->usbinfo.devlock);
+
 		return LIBUSB_ERROR_NO_DEVICE;
 	}
 	usbdev = cgpu->usbdev;
@@ -2494,6 +2533,8 @@ int _usb_transfer(struct cgpu_info *cgpu, uint8_t request_type, uint8_t bRequest
 	if (NODEV(err))
 		release_cgpu(cgpu);
 
+	wr_unlock(cgpu->usbinfo.devlock);
+
 	return err;
 }
 
@@ -2505,11 +2546,15 @@ int _usb_transfer_read(struct cgpu_info *cgpu, uint8_t request_type, uint8_t bRe
 #endif
 	int err;
 
+	wr_lock(cgpu->usbinfo.devlock);
+
 	USBDEBUG("USB debug: _usb_transfer_read(%s (nodev=%s),type=%"PRIu8",req=%"PRIu8",value=%"PRIu16",index=%"PRIu16",bufsiz=%d,timeout=%u,cmd=%s)", cgpu->drv->name, bool_str(cgpu->usbinfo.nodev), request_type, bRequest, wValue, wIndex, bufsiz, timeout, usb_cmdname(cmd));
 
 	if (cgpu->usbinfo.nodev) {
 		USB_REJECT(cgpu, MODE_CTRL_READ);
 
+		wr_unlock(cgpu->usbinfo.devlock);
+
 		return LIBUSB_ERROR_NO_DEVICE;
 	}
 	usbdev = cgpu->usbdev;
@@ -2534,6 +2579,8 @@ int _usb_transfer_read(struct cgpu_info *cgpu, uint8_t request_type, uint8_t bRe
 	} else if (NODEV(err))
 		release_cgpu(cgpu);
 
+	wr_unlock(cgpu->usbinfo.devlock);
+
 	return err;
 }
 
@@ -2647,7 +2694,9 @@ void usb_cleanup()
 			case DRIVER_MODMINER:
 			case DRIVER_ICARUS:
 			case DRIVER_AVALON:
+				wr_lock(cgpu->usbinfo.devlock);
 				release_cgpu(cgpu);
+				wr_unlock(cgpu->usbinfo.devlock);
 				count++;
 				break;
 			default:
diff --git a/usbutils.h b/usbutils.h
index 96f2b84..e9c794a 100644
--- a/usbutils.h
+++ b/usbutils.h
@@ -188,6 +188,17 @@ struct cg_usb_info {
 	struct timeval last_nodev;
 	uint32_t ioerr_count;
 	uint32_t continuous_ioerr_count;
+
+	/*
+	 * for nodev and cgusb access (read and write)
+	 * it's a pointer so MMQ can have it in multiple devices
+	 *
+	 * N.B. general mining code doesn't need to use the read
+	 * lock for 'nodev' if it calls a usb_read/write/etc function
+	 * that uses the lock - however, all usbutils code MUST use it
+	 * to avoid devices disappearing while in use by multiple threads
+	 */
+	pthread_rwlock_t *devlock;
 };
 
 enum usb_cmds {
@@ -262,7 +273,8 @@ const char *usb_cmdname(enum usb_cmds cmd);
 void usb_applog(struct cgpu_info *bflsc, enum usb_cmds cmd, char *msg, int amount, int err);
 struct cgpu_info *usb_copy_cgpu(struct cgpu_info *orig);
 struct cgpu_info *usb_alloc_cgpu(struct device_drv *drv, int threads);
-struct cgpu_info *usb_free_cgpu(struct cgpu_info *cgpu);
+struct cgpu_info *usb_free_cgpu_devlock(struct cgpu_info *cgpu, bool free_devlock);
+#define usb_free_cgpu(cgpu) usb_free_cgpu_devlock(cgpu, true)
 void usb_uninit(struct cgpu_info *cgpu);
 bool usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct usb_find_devices *found);
 void usb_detect(struct device_drv *drv, bool (*device_detect)(struct libusb_device *, struct usb_find_devices *));