diff --git a/driver-modminer.c b/driver-modminer.c index 1519bc9b..581016e9 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 fd3859d6..d43e34c3 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 96f2b84b..e9c794a8 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 *));