Browse Source

USB system wide device locking on linux

nfactor-troky
Kano 12 years ago
parent
commit
cb3e3ebfac
  1. 286
      usbutils.c

286
usbutils.c

@ -110,24 +110,10 @@ extern struct device_drv icarus_drv;
extern struct device_drv modminer_drv; extern struct device_drv modminer_drv;
#endif #endif
/*
* Our own internal list of used USB devices
* So two drivers or a single driver searching
* can't touch the same device during detection
*/
struct usb_list {
uint8_t bus_number;
uint8_t device_address;
uint8_t filler[2];
struct usb_list *prev;
struct usb_list *next;
};
#define STRBUFLEN 256 #define STRBUFLEN 256
static const char *BLANK = ""; static const char *BLANK = "";
static pthread_mutex_t *list_lock = NULL; static bool stats_initialised = false;
static struct usb_list *usb_head = NULL;
struct cg_usb_stats_item { struct cg_usb_stats_item {
uint64_t count; uint64_t count;
@ -537,10 +523,7 @@ static void cgusb_check_init()
{ {
mutex_lock(&cgusb_lock); mutex_lock(&cgusb_lock);
if (list_lock == NULL) { if (stats_initialised == false) {
list_lock = calloc(1, sizeof(*list_lock));
mutex_init(list_lock);
// N.B. environment LIBUSB_DEBUG also sets libusb_set_debug() // N.B. environment LIBUSB_DEBUG also sets libusb_set_debug()
if (opt_usbdump >= 0) { if (opt_usbdump >= 0) {
libusb_set_debug(NULL, opt_usbdump); libusb_set_debug(NULL, opt_usbdump);
@ -592,134 +575,163 @@ static void cgusb_check_init()
mutex_unlock(&cgusb_lock); mutex_unlock(&cgusb_lock);
} }
static bool in_use(libusb_device *dev, bool lock) #ifdef WIN32
{ #else
struct usb_list *usb_tmp; #include <errno.h>
bool used = false; #include <unistd.h>
uint8_t bus_number; #include <sys/types.h>
uint8_t device_address; #include <sys/ipc.h>
#include <sys/sem.h>
bus_number = libusb_get_bus_number(dev); #include <sys/types.h>
device_address = libusb_get_device_address(dev); #include <sys/stat.h>
#include <fcntl.h>
if (lock)
mutex_lock(list_lock); union semun {
int sem;
if ((usb_tmp = usb_head)) struct semid_ds *seminfo;
do { ushort *all;
if (bus_number == usb_tmp->bus_number };
&& device_address == usb_tmp->device_address) { #endif
used = true;
break;
}
usb_tmp = usb_tmp->next;
} while (usb_tmp != usb_head);
if (lock)
mutex_unlock(list_lock);
return used;
}
static void add_used(libusb_device *dev, bool lock) // Any errors should always be printed since they will rarely if ever occur
// and thus it is best to always display them
static bool cgminer_usb_lock_bd(struct device_drv *drv, uint8_t bus_number, uint8_t device_address)
{ {
struct usb_list *usb_tmp; #ifdef WIN32
char buf[128]; #else
uint8_t bus_number; struct semid_ds seminfo;
uint8_t device_address; union semun opt;
char name[64];
bus_number = libusb_get_bus_number(dev); key_t key;
device_address = libusb_get_device_address(dev); int fd, sem, count;
if (lock) sprintf(name, "/tmp/cgminer-usb-%d-%d", (int)bus_number, (int)device_address);
mutex_lock(list_lock); fd = open(name, O_CREAT|O_RDONLY, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
if (fd == -1) {
applog(LOG_ERR,
"SEM: %s USB open failed '%s' err (%d) %s",
drv->dname, name, errno, strerror(errno));
return false;
}
close(fd);
key = ftok(name, 'K');
sem = semget(key, 1, IPC_CREAT | IPC_EXCL | 438);
if (sem < 0) {
if (errno != EEXIST) {
applog(LOG_ERR,
"SEM: %s USB failed to get '%s' err (%d) %s",
drv->dname, name, errno, strerror(errno));
return false;
}
if (in_use(dev, false)) { sem = semget(key, 1, 0);
if (lock) if (sem < 0) {
mutex_unlock(list_lock); applog(LOG_ERR,
"SEM: %s USB failed to access '%s' err (%d) %s",
drv->dname, name, errno, strerror(errno));
return false;
}
sprintf(buf, "add_used() duplicate bus_number %d device_address %d", opt.seminfo = &seminfo;
(int)bus_number, (int)device_address); count = 0;
quit(1, buf); while (++count) {
// Should NEVER take 100ms
if (count > 99) {
applog(LOG_ERR,
"SEM: %s USB timeout waiting for (%d) '%s'",
drv->dname, sem, name);
return false;
}
if (semctl(sem, 0, IPC_STAT, opt) == -1) {
applog(LOG_ERR,
"SEM: %s USB failed to wait for (%d) '%s' count %d err (%d) %s",
drv->dname, sem, name, count, errno, strerror(errno));
return false;
}
if (opt.seminfo->sem_otime != 0)
break;
nmsleep(1);
}
} }
usb_tmp = malloc(sizeof(*usb_tmp)); struct sembuf sops[] = {
{ 0, 0, IPC_NOWAIT | SEM_UNDO },
usb_tmp->bus_number = bus_number; { 0, 1, IPC_NOWAIT | SEM_UNDO }
usb_tmp->device_address = device_address; };
if (usb_head) { if (semop(sem, sops, 2)) {
// add to end if (errno == EAGAIN) {
usb_tmp->prev = usb_head->prev; applog(LOG_WARNING,
usb_tmp->next = usb_head; "SEM: %s USB failed to get (%d) '%s' - device in use",
usb_head->prev = usb_tmp; drv->dname, sem, name);
usb_tmp->prev->next = usb_tmp; } else {
} else { applog(LOG_DEBUG,
usb_tmp->prev = usb_tmp; "SEM: %s USB failed to get (%d) '%s' err(%d) %s",
usb_tmp->next = usb_tmp; drv->dname, sem, name, errno, strerror(errno));
usb_head = usb_tmp; }
return false;
} }
if (lock) return true;
mutex_unlock(list_lock); #endif
} }
static void release(uint8_t bus_number, uint8_t device_address, bool lock) static bool cgminer_usb_lock(struct device_drv *drv, libusb_device *dev)
{ {
struct usb_list *usb_tmp; return cgminer_usb_lock_bd(drv, libusb_get_bus_number(dev), libusb_get_device_address(dev));
bool found = false; }
char buf[128];
if (lock)
mutex_lock(list_lock);
if ((usb_tmp = usb_head))
do {
if (bus_number == usb_tmp->bus_number
&& device_address == usb_tmp->device_address) {
found = true;
break;
}
usb_tmp = usb_tmp->next;
} while (usb_tmp != usb_head); // Any errors should always be printed since they will rarely if ever occur
// and thus it is best to always display them
static void cgminer_usb_unlock_bd(struct device_drv *drv, uint8_t bus_number, uint8_t device_address)
{
#ifdef WIN32
#else
char name[64];
key_t key;
int fd, sem;
sprintf(name, "/tmp/cgminer-usb-%d-%d", (int)bus_number, (int)device_address);
fd = open(name, O_CREAT|O_RDONLY, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
if (fd == -1) {
applog(LOG_ERR,
"SEM: %s USB open failed '%s' for release err (%d) %s",
drv->dname, name, errno, strerror(errno));
return;
}
close(fd);
key = ftok(name, 'K');
sem = semget(key, 1, 0);
if (sem < 0) {
applog(LOG_ERR,
"SEM: %s USB failed to get '%s' for release err (%d) %s",
drv->dname, name, errno, strerror(errno));
return;
}
if (!found) { struct sembuf sops[] = {
if (lock) { 0, -1, SEM_UNDO }
mutex_unlock(list_lock); };
sprintf(buf, "release() unknown: bus_number %d device_address %d", // Allow a 10ms timeout
(int)bus_number, (int)device_address); // exceeding this timeout means it would probably never succeed anyway
quit(1, buf); struct timespec timeout = { 0, 10000000 };
}
if (usb_tmp->next == usb_tmp) { // Wait forever since we shoud be the one who has it
usb_head = NULL; if (semtimedop(sem, sops, 1, &timeout)) {
} else { applog(LOG_ERR,
if (usb_head == usb_tmp) "SEM: %d USB failed to release '%s' err (%d) %s",
usb_head = usb_tmp->next; drv->dname, name, errno, strerror(errno));
usb_tmp->next->prev = usb_tmp->prev;
usb_tmp->prev->next = usb_tmp->next;
} }
if (lock) return;
mutex_unlock(list_lock); #endif
free(usb_tmp);
} }
static void release_dev(libusb_device *dev, bool lock) static void cgminer_usb_unlock(struct device_drv *drv, libusb_device *dev)
{ {
uint8_t bus_number; cgminer_usb_unlock_bd(drv, libusb_get_bus_number(dev), libusb_get_device_address(dev));
uint8_t device_address;
bus_number = libusb_get_bus_number(dev);
device_address = libusb_get_device_address(dev);
release(bus_number, device_address, lock);
} }
static struct cg_usb_device *free_cgusb(struct cg_usb_device *cgusb) static struct cg_usb_device *free_cgusb(struct cg_usb_device *cgusb)
@ -771,7 +783,7 @@ void release_cgpu(struct cgpu_info *cgpu)
usb_uninit(cgpu); usb_uninit(cgpu);
release(cgpu->usbinfo.bus_number, cgpu->usbinfo.device_address, true); cgminer_usb_unlock_bd(cgpu->drv, cgpu->usbinfo.bus_number, cgpu->usbinfo.device_address);
} }
bool usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct usb_find_devices *found) bool usb_init(struct cgpu_info *cgpu, struct libusb_device *dev, struct usb_find_devices *found)
@ -1053,21 +1065,11 @@ void usb_detect(struct device_drv *drv, bool (*device_detect)(struct libusb_devi
applog(LOG_DEBUG, "USB scan devices: found no devices"); applog(LOG_DEBUG, "USB scan devices: found no devices");
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
mutex_lock(list_lock); found = usb_check(drv, list[i]);
if (found) {
if (in_use(list[i], false)) if (cgminer_usb_lock(drv, list[i]) == true)
mutex_unlock(list_lock);
else {
add_used(list[i], false);
mutex_unlock(list_lock);
found = usb_check(drv, list[i]);
if (!found)
release_dev(list[i], true);
else
if (!device_detect(list[i], found)) if (!device_detect(list[i], found))
release_dev(list[i], true); cgminer_usb_unlock(drv, list[i]);
} }
} }

Loading…
Cancel
Save