|
@@ -14,6 +14,7 @@
|
|
#define DRIVER_DESC "USB/IP Host Driver"
|
|
#define DRIVER_DESC "USB/IP Host Driver"
|
|
|
|
|
|
struct kmem_cache *stub_priv_cache;
|
|
struct kmem_cache *stub_priv_cache;
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* busid_tables defines matching busids that usbip can grab. A user can change
|
|
* busid_tables defines matching busids that usbip can grab. A user can change
|
|
* dynamically what device is locally used and what device is exported to a
|
|
* dynamically what device is locally used and what device is exported to a
|
|
@@ -25,6 +26,8 @@ static spinlock_t busid_table_lock;
|
|
|
|
|
|
static void init_busid_table(void)
|
|
static void init_busid_table(void)
|
|
{
|
|
{
|
|
|
|
+ int i;
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* This also sets the bus_table[i].status to
|
|
* This also sets the bus_table[i].status to
|
|
* STUB_BUSID_OTHER, which is 0.
|
|
* STUB_BUSID_OTHER, which is 0.
|
|
@@ -32,6 +35,9 @@ static void init_busid_table(void)
|
|
memset(busid_table, 0, sizeof(busid_table));
|
|
memset(busid_table, 0, sizeof(busid_table));
|
|
|
|
|
|
spin_lock_init(&busid_table_lock);
|
|
spin_lock_init(&busid_table_lock);
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < MAX_BUSID; i++)
|
|
|
|
+ spin_lock_init(&busid_table[i].busid_lock);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -43,15 +49,20 @@ static int get_busid_idx(const char *busid)
|
|
int i;
|
|
int i;
|
|
int idx = -1;
|
|
int idx = -1;
|
|
|
|
|
|
- for (i = 0; i < MAX_BUSID; i++)
|
|
|
|
|
|
+ for (i = 0; i < MAX_BUSID; i++) {
|
|
|
|
+ spin_lock(&busid_table[i].busid_lock);
|
|
if (busid_table[i].name[0])
|
|
if (busid_table[i].name[0])
|
|
if (!strncmp(busid_table[i].name, busid, BUSID_SIZE)) {
|
|
if (!strncmp(busid_table[i].name, busid, BUSID_SIZE)) {
|
|
idx = i;
|
|
idx = i;
|
|
|
|
+ spin_unlock(&busid_table[i].busid_lock);
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
|
|
+ spin_unlock(&busid_table[i].busid_lock);
|
|
|
|
+ }
|
|
return idx;
|
|
return idx;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/* Returns holding busid_lock. Should call put_busid_priv() to unlock */
|
|
struct bus_id_priv *get_busid_priv(const char *busid)
|
|
struct bus_id_priv *get_busid_priv(const char *busid)
|
|
{
|
|
{
|
|
int idx;
|
|
int idx;
|
|
@@ -59,13 +70,22 @@ struct bus_id_priv *get_busid_priv(const char *busid)
|
|
|
|
|
|
spin_lock(&busid_table_lock);
|
|
spin_lock(&busid_table_lock);
|
|
idx = get_busid_idx(busid);
|
|
idx = get_busid_idx(busid);
|
|
- if (idx >= 0)
|
|
|
|
|
|
+ if (idx >= 0) {
|
|
bid = &(busid_table[idx]);
|
|
bid = &(busid_table[idx]);
|
|
|
|
+ /* get busid_lock before returning */
|
|
|
|
+ spin_lock(&bid->busid_lock);
|
|
|
|
+ }
|
|
spin_unlock(&busid_table_lock);
|
|
spin_unlock(&busid_table_lock);
|
|
|
|
|
|
return bid;
|
|
return bid;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+void put_busid_priv(struct bus_id_priv *bid)
|
|
|
|
+{
|
|
|
|
+ if (bid)
|
|
|
|
+ spin_unlock(&bid->busid_lock);
|
|
|
|
+}
|
|
|
|
+
|
|
static int add_match_busid(char *busid)
|
|
static int add_match_busid(char *busid)
|
|
{
|
|
{
|
|
int i;
|
|
int i;
|
|
@@ -78,15 +98,19 @@ static int add_match_busid(char *busid)
|
|
goto out;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
|
|
- for (i = 0; i < MAX_BUSID; i++)
|
|
|
|
|
|
+ for (i = 0; i < MAX_BUSID; i++) {
|
|
|
|
+ spin_lock(&busid_table[i].busid_lock);
|
|
if (!busid_table[i].name[0]) {
|
|
if (!busid_table[i].name[0]) {
|
|
strlcpy(busid_table[i].name, busid, BUSID_SIZE);
|
|
strlcpy(busid_table[i].name, busid, BUSID_SIZE);
|
|
if ((busid_table[i].status != STUB_BUSID_ALLOC) &&
|
|
if ((busid_table[i].status != STUB_BUSID_ALLOC) &&
|
|
(busid_table[i].status != STUB_BUSID_REMOV))
|
|
(busid_table[i].status != STUB_BUSID_REMOV))
|
|
busid_table[i].status = STUB_BUSID_ADDED;
|
|
busid_table[i].status = STUB_BUSID_ADDED;
|
|
ret = 0;
|
|
ret = 0;
|
|
|
|
+ spin_unlock(&busid_table[i].busid_lock);
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
|
|
+ spin_unlock(&busid_table[i].busid_lock);
|
|
|
|
+ }
|
|
|
|
|
|
out:
|
|
out:
|
|
spin_unlock(&busid_table_lock);
|
|
spin_unlock(&busid_table_lock);
|
|
@@ -107,6 +131,8 @@ int del_match_busid(char *busid)
|
|
/* found */
|
|
/* found */
|
|
ret = 0;
|
|
ret = 0;
|
|
|
|
|
|
|
|
+ spin_lock(&busid_table[idx].busid_lock);
|
|
|
|
+
|
|
if (busid_table[idx].status == STUB_BUSID_OTHER)
|
|
if (busid_table[idx].status == STUB_BUSID_OTHER)
|
|
memset(busid_table[idx].name, 0, BUSID_SIZE);
|
|
memset(busid_table[idx].name, 0, BUSID_SIZE);
|
|
|
|
|
|
@@ -114,6 +140,7 @@ int del_match_busid(char *busid)
|
|
(busid_table[idx].status != STUB_BUSID_ADDED))
|
|
(busid_table[idx].status != STUB_BUSID_ADDED))
|
|
busid_table[idx].status = STUB_BUSID_REMOV;
|
|
busid_table[idx].status = STUB_BUSID_REMOV;
|
|
|
|
|
|
|
|
+ spin_unlock(&busid_table[idx].busid_lock);
|
|
out:
|
|
out:
|
|
spin_unlock(&busid_table_lock);
|
|
spin_unlock(&busid_table_lock);
|
|
|
|
|
|
@@ -126,9 +153,12 @@ static ssize_t match_busid_show(struct device_driver *drv, char *buf)
|
|
char *out = buf;
|
|
char *out = buf;
|
|
|
|
|
|
spin_lock(&busid_table_lock);
|
|
spin_lock(&busid_table_lock);
|
|
- for (i = 0; i < MAX_BUSID; i++)
|
|
|
|
|
|
+ for (i = 0; i < MAX_BUSID; i++) {
|
|
|
|
+ spin_lock(&busid_table[i].busid_lock);
|
|
if (busid_table[i].name[0])
|
|
if (busid_table[i].name[0])
|
|
out += sprintf(out, "%s ", busid_table[i].name);
|
|
out += sprintf(out, "%s ", busid_table[i].name);
|
|
|
|
+ spin_unlock(&busid_table[i].busid_lock);
|
|
|
|
+ }
|
|
spin_unlock(&busid_table_lock);
|
|
spin_unlock(&busid_table_lock);
|
|
out += sprintf(out, "\n");
|
|
out += sprintf(out, "\n");
|
|
|
|
|
|
@@ -169,6 +199,51 @@ static ssize_t match_busid_store(struct device_driver *dev, const char *buf,
|
|
}
|
|
}
|
|
static DRIVER_ATTR_RW(match_busid);
|
|
static DRIVER_ATTR_RW(match_busid);
|
|
|
|
|
|
|
|
+static int do_rebind(char *busid, struct bus_id_priv *busid_priv)
|
|
|
|
+{
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ /* device_attach() callers should hold parent lock for USB */
|
|
|
|
+ if (busid_priv->udev->dev.parent)
|
|
|
|
+ device_lock(busid_priv->udev->dev.parent);
|
|
|
|
+ ret = device_attach(&busid_priv->udev->dev);
|
|
|
|
+ if (busid_priv->udev->dev.parent)
|
|
|
|
+ device_unlock(busid_priv->udev->dev.parent);
|
|
|
|
+ if (ret < 0) {
|
|
|
|
+ dev_err(&busid_priv->udev->dev, "rebind failed\n");
|
|
|
|
+ return ret;
|
|
|
|
+ }
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void stub_device_rebind(void)
|
|
|
|
+{
|
|
|
|
+#if IS_MODULE(CONFIG_USBIP_HOST)
|
|
|
|
+ struct bus_id_priv *busid_priv;
|
|
|
|
+ int i;
|
|
|
|
+
|
|
|
|
+ /* update status to STUB_BUSID_OTHER so probe ignores the device */
|
|
|
|
+ spin_lock(&busid_table_lock);
|
|
|
|
+ for (i = 0; i < MAX_BUSID; i++) {
|
|
|
|
+ if (busid_table[i].name[0] &&
|
|
|
|
+ busid_table[i].shutdown_busid) {
|
|
|
|
+ busid_priv = &(busid_table[i]);
|
|
|
|
+ busid_priv->status = STUB_BUSID_OTHER;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ spin_unlock(&busid_table_lock);
|
|
|
|
+
|
|
|
|
+ /* now run rebind - no need to hold locks. driver files are removed */
|
|
|
|
+ for (i = 0; i < MAX_BUSID; i++) {
|
|
|
|
+ if (busid_table[i].name[0] &&
|
|
|
|
+ busid_table[i].shutdown_busid) {
|
|
|
|
+ busid_priv = &(busid_table[i]);
|
|
|
|
+ do_rebind(busid_table[i].name, busid_priv);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+#endif
|
|
|
|
+}
|
|
|
|
+
|
|
static ssize_t rebind_store(struct device_driver *dev, const char *buf,
|
|
static ssize_t rebind_store(struct device_driver *dev, const char *buf,
|
|
size_t count)
|
|
size_t count)
|
|
{
|
|
{
|
|
@@ -186,16 +261,17 @@ static ssize_t rebind_store(struct device_driver *dev, const char *buf,
|
|
if (!bid)
|
|
if (!bid)
|
|
return -ENODEV;
|
|
return -ENODEV;
|
|
|
|
|
|
- /* device_attach() callers should hold parent lock for USB */
|
|
|
|
- if (bid->udev->dev.parent)
|
|
|
|
- device_lock(bid->udev->dev.parent);
|
|
|
|
- ret = device_attach(&bid->udev->dev);
|
|
|
|
- if (bid->udev->dev.parent)
|
|
|
|
- device_unlock(bid->udev->dev.parent);
|
|
|
|
- if (ret < 0) {
|
|
|
|
- dev_err(&bid->udev->dev, "rebind failed\n");
|
|
|
|
|
|
+ /* mark the device for deletion so probe ignores it during rescan */
|
|
|
|
+ bid->status = STUB_BUSID_OTHER;
|
|
|
|
+ /* release the busid lock */
|
|
|
|
+ put_busid_priv(bid);
|
|
|
|
+
|
|
|
|
+ ret = do_rebind((char *) buf, bid);
|
|
|
|
+ if (ret < 0)
|
|
return ret;
|
|
return ret;
|
|
- }
|
|
|
|
|
|
+
|
|
|
|
+ /* delete device from busid_table */
|
|
|
|
+ del_match_busid((char *) buf);
|
|
|
|
|
|
return count;
|
|
return count;
|
|
}
|
|
}
|
|
@@ -317,6 +393,9 @@ static void __exit usbip_host_exit(void)
|
|
*/
|
|
*/
|
|
usb_deregister_device_driver(&stub_driver);
|
|
usb_deregister_device_driver(&stub_driver);
|
|
|
|
|
|
|
|
+ /* initiate scan to attach devices */
|
|
|
|
+ stub_device_rebind();
|
|
|
|
+
|
|
kmem_cache_destroy(stub_priv_cache);
|
|
kmem_cache_destroy(stub_priv_cache);
|
|
}
|
|
}
|
|
|
|
|