aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/usbip/stub_main.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/usbip/stub_main.c')
-rw-r--r--drivers/usb/usbip/stub_main.c105
1 files changed, 92 insertions, 13 deletions
diff --git a/drivers/usb/usbip/stub_main.c b/drivers/usb/usbip/stub_main.c
index d41d0cdeec0f..bf8a5feb0ee9 100644
--- a/drivers/usb/usbip/stub_main.c
+++ b/drivers/usb/usbip/stub_main.c
@@ -14,6 +14,7 @@
14#define DRIVER_DESC "USB/IP Host Driver" 14#define DRIVER_DESC "USB/IP Host Driver"
15 15
16struct kmem_cache *stub_priv_cache; 16struct kmem_cache *stub_priv_cache;
17
17/* 18/*
18 * busid_tables defines matching busids that usbip can grab. A user can change 19 * busid_tables defines matching busids that usbip can grab. A user can change
19 * dynamically what device is locally used and what device is exported to a 20 * dynamically what device is locally used and what device is exported to a
@@ -25,6 +26,8 @@ static spinlock_t busid_table_lock;
25 26
26static void init_busid_table(void) 27static void init_busid_table(void)
27{ 28{
29 int i;
30
28 /* 31 /*
29 * This also sets the bus_table[i].status to 32 * This also sets the bus_table[i].status to
30 * STUB_BUSID_OTHER, which is 0. 33 * STUB_BUSID_OTHER, which is 0.
@@ -32,6 +35,9 @@ static void init_busid_table(void)
32 memset(busid_table, 0, sizeof(busid_table)); 35 memset(busid_table, 0, sizeof(busid_table));
33 36
34 spin_lock_init(&busid_table_lock); 37 spin_lock_init(&busid_table_lock);
38
39 for (i = 0; i < MAX_BUSID; i++)
40 spin_lock_init(&busid_table[i].busid_lock);
35} 41}
36 42
37/* 43/*
@@ -43,15 +49,20 @@ static int get_busid_idx(const char *busid)
43 int i; 49 int i;
44 int idx = -1; 50 int idx = -1;
45 51
46 for (i = 0; i < MAX_BUSID; i++) 52 for (i = 0; i < MAX_BUSID; i++) {
53 spin_lock(&busid_table[i].busid_lock);
47 if (busid_table[i].name[0]) 54 if (busid_table[i].name[0])
48 if (!strncmp(busid_table[i].name, busid, BUSID_SIZE)) { 55 if (!strncmp(busid_table[i].name, busid, BUSID_SIZE)) {
49 idx = i; 56 idx = i;
57 spin_unlock(&busid_table[i].busid_lock);
50 break; 58 break;
51 } 59 }
60 spin_unlock(&busid_table[i].busid_lock);
61 }
52 return idx; 62 return idx;
53} 63}
54 64
65/* Returns holding busid_lock. Should call put_busid_priv() to unlock */
55struct bus_id_priv *get_busid_priv(const char *busid) 66struct bus_id_priv *get_busid_priv(const char *busid)
56{ 67{
57 int idx; 68 int idx;
@@ -59,13 +70,22 @@ struct bus_id_priv *get_busid_priv(const char *busid)
59 70
60 spin_lock(&busid_table_lock); 71 spin_lock(&busid_table_lock);
61 idx = get_busid_idx(busid); 72 idx = get_busid_idx(busid);
62 if (idx >= 0) 73 if (idx >= 0) {
63 bid = &(busid_table[idx]); 74 bid = &(busid_table[idx]);
75 /* get busid_lock before returning */
76 spin_lock(&bid->busid_lock);
77 }
64 spin_unlock(&busid_table_lock); 78 spin_unlock(&busid_table_lock);
65 79
66 return bid; 80 return bid;
67} 81}
68 82
83void put_busid_priv(struct bus_id_priv *bid)
84{
85 if (bid)
86 spin_unlock(&bid->busid_lock);
87}
88
69static int add_match_busid(char *busid) 89static int add_match_busid(char *busid)
70{ 90{
71 int i; 91 int i;
@@ -78,15 +98,19 @@ static int add_match_busid(char *busid)
78 goto out; 98 goto out;
79 } 99 }
80 100
81 for (i = 0; i < MAX_BUSID; i++) 101 for (i = 0; i < MAX_BUSID; i++) {
102 spin_lock(&busid_table[i].busid_lock);
82 if (!busid_table[i].name[0]) { 103 if (!busid_table[i].name[0]) {
83 strlcpy(busid_table[i].name, busid, BUSID_SIZE); 104 strlcpy(busid_table[i].name, busid, BUSID_SIZE);
84 if ((busid_table[i].status != STUB_BUSID_ALLOC) && 105 if ((busid_table[i].status != STUB_BUSID_ALLOC) &&
85 (busid_table[i].status != STUB_BUSID_REMOV)) 106 (busid_table[i].status != STUB_BUSID_REMOV))
86 busid_table[i].status = STUB_BUSID_ADDED; 107 busid_table[i].status = STUB_BUSID_ADDED;
87 ret = 0; 108 ret = 0;
109 spin_unlock(&busid_table[i].busid_lock);
88 break; 110 break;
89 } 111 }
112 spin_unlock(&busid_table[i].busid_lock);
113 }
90 114
91out: 115out:
92 spin_unlock(&busid_table_lock); 116 spin_unlock(&busid_table_lock);
@@ -107,6 +131,8 @@ int del_match_busid(char *busid)
107 /* found */ 131 /* found */
108 ret = 0; 132 ret = 0;
109 133
134 spin_lock(&busid_table[idx].busid_lock);
135
110 if (busid_table[idx].status == STUB_BUSID_OTHER) 136 if (busid_table[idx].status == STUB_BUSID_OTHER)
111 memset(busid_table[idx].name, 0, BUSID_SIZE); 137 memset(busid_table[idx].name, 0, BUSID_SIZE);
112 138
@@ -114,6 +140,7 @@ int del_match_busid(char *busid)
114 (busid_table[idx].status != STUB_BUSID_ADDED)) 140 (busid_table[idx].status != STUB_BUSID_ADDED))
115 busid_table[idx].status = STUB_BUSID_REMOV; 141 busid_table[idx].status = STUB_BUSID_REMOV;
116 142
143 spin_unlock(&busid_table[idx].busid_lock);
117out: 144out:
118 spin_unlock(&busid_table_lock); 145 spin_unlock(&busid_table_lock);
119 146
@@ -126,9 +153,12 @@ static ssize_t match_busid_show(struct device_driver *drv, char *buf)
126 char *out = buf; 153 char *out = buf;
127 154
128 spin_lock(&busid_table_lock); 155 spin_lock(&busid_table_lock);
129 for (i = 0; i < MAX_BUSID; i++) 156 for (i = 0; i < MAX_BUSID; i++) {
157 spin_lock(&busid_table[i].busid_lock);
130 if (busid_table[i].name[0]) 158 if (busid_table[i].name[0])
131 out += sprintf(out, "%s ", busid_table[i].name); 159 out += sprintf(out, "%s ", busid_table[i].name);
160 spin_unlock(&busid_table[i].busid_lock);
161 }
132 spin_unlock(&busid_table_lock); 162 spin_unlock(&busid_table_lock);
133 out += sprintf(out, "\n"); 163 out += sprintf(out, "\n");
134 164
@@ -169,6 +199,51 @@ static ssize_t match_busid_store(struct device_driver *dev, const char *buf,
169} 199}
170static DRIVER_ATTR_RW(match_busid); 200static DRIVER_ATTR_RW(match_busid);
171 201
202static int do_rebind(char *busid, struct bus_id_priv *busid_priv)
203{
204 int ret;
205
206 /* device_attach() callers should hold parent lock for USB */
207 if (busid_priv->udev->dev.parent)
208 device_lock(busid_priv->udev->dev.parent);
209 ret = device_attach(&busid_priv->udev->dev);
210 if (busid_priv->udev->dev.parent)
211 device_unlock(busid_priv->udev->dev.parent);
212 if (ret < 0) {
213 dev_err(&busid_priv->udev->dev, "rebind failed\n");
214 return ret;
215 }
216 return 0;
217}
218
219static void stub_device_rebind(void)
220{
221#if IS_MODULE(CONFIG_USBIP_HOST)
222 struct bus_id_priv *busid_priv;
223 int i;
224
225 /* update status to STUB_BUSID_OTHER so probe ignores the device */
226 spin_lock(&busid_table_lock);
227 for (i = 0; i < MAX_BUSID; i++) {
228 if (busid_table[i].name[0] &&
229 busid_table[i].shutdown_busid) {
230 busid_priv = &(busid_table[i]);
231 busid_priv->status = STUB_BUSID_OTHER;
232 }
233 }
234 spin_unlock(&busid_table_lock);
235
236 /* now run rebind - no need to hold locks. driver files are removed */
237 for (i = 0; i < MAX_BUSID; i++) {
238 if (busid_table[i].name[0] &&
239 busid_table[i].shutdown_busid) {
240 busid_priv = &(busid_table[i]);
241 do_rebind(busid_table[i].name, busid_priv);
242 }
243 }
244#endif
245}
246
172static ssize_t rebind_store(struct device_driver *dev, const char *buf, 247static ssize_t rebind_store(struct device_driver *dev, const char *buf,
173 size_t count) 248 size_t count)
174{ 249{
@@ -186,16 +261,17 @@ static ssize_t rebind_store(struct device_driver *dev, const char *buf,
186 if (!bid) 261 if (!bid)
187 return -ENODEV; 262 return -ENODEV;
188 263
189 /* device_attach() callers should hold parent lock for USB */ 264 /* mark the device for deletion so probe ignores it during rescan */
190 if (bid->udev->dev.parent) 265 bid->status = STUB_BUSID_OTHER;
191 device_lock(bid->udev->dev.parent); 266 /* release the busid lock */
192 ret = device_attach(&bid->udev->dev); 267 put_busid_priv(bid);
193 if (bid->udev->dev.parent) 268
194 device_unlock(bid->udev->dev.parent); 269 ret = do_rebind((char *) buf, bid);
195 if (ret < 0) { 270 if (ret < 0)
196 dev_err(&bid->udev->dev, "rebind failed\n");
197 return ret; 271 return ret;
198 } 272
273 /* delete device from busid_table */
274 del_match_busid((char *) buf);
199 275
200 return count; 276 return count;
201} 277}
@@ -317,6 +393,9 @@ static void __exit usbip_host_exit(void)
317 */ 393 */
318 usb_deregister_device_driver(&stub_driver); 394 usb_deregister_device_driver(&stub_driver);
319 395
396 /* initiate scan to attach devices */
397 stub_device_rebind();
398
320 kmem_cache_destroy(stub_priv_cache); 399 kmem_cache_destroy(stub_priv_cache);
321} 400}
322 401