diff options
Diffstat (limited to 'drivers/usb/usbip/stub_main.c')
-rw-r--r-- | drivers/usb/usbip/stub_main.c | 105 |
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 | ||
16 | struct kmem_cache *stub_priv_cache; | 16 | struct 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 | ||
26 | static void init_busid_table(void) | 27 | static 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 */ | ||
55 | struct bus_id_priv *get_busid_priv(const char *busid) | 66 | struct 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 | ||
83 | void put_busid_priv(struct bus_id_priv *bid) | ||
84 | { | ||
85 | if (bid) | ||
86 | spin_unlock(&bid->busid_lock); | ||
87 | } | ||
88 | |||
69 | static int add_match_busid(char *busid) | 89 | static 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 | ||
91 | out: | 115 | out: |
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); | ||
117 | out: | 144 | out: |
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 | } |
170 | static DRIVER_ATTR_RW(match_busid); | 200 | static DRIVER_ATTR_RW(match_busid); |
171 | 201 | ||
202 | static 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 | |||
219 | static 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 | |||
172 | static ssize_t rebind_store(struct device_driver *dev, const char *buf, | 247 | static 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 | ||