Skip to content

Commit 2ad8e5e

Browse files
committed
fix BSOD occurred when detaching a busy device
Old plugout was prone to incur BSOD when a very busy device is detached. Fiio device suffers from such the problem(see comment in #222). Indeed, there was no locking between vusb I/O and plugout. To resolve this issue, get/put for vusb has been introduced with a reference count.
1 parent dec9d9b commit 2ad8e5e

13 files changed

+154
-104
lines changed

driver/vhci_ude/usbip_vhci_ude.vcxproj

+2-1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
<ClCompile Include="vhci_queue_ep.c" />
4848
<ClCompile Include="vhci_urbr_store_status.c" />
4949
<ClCompile Include="vhci_urbr_store_vendor.c" />
50+
<ClCompile Include="vhci_vusb.c" />
5051
<ClCompile Include="vhci_write.c" />
5152
</ItemGroup>
5253
<ItemGroup>
@@ -258,4 +259,4 @@
258259
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
259260
<ImportGroup Label="ExtensionTargets">
260261
</ImportGroup>
261-
</Project>
262+
</Project>

driver/vhci_ude/usbip_vhci_ude.vcxproj.filters

+3
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,9 @@
123123
<ClCompile Include="vhci_urbr_store_reset.c">
124124
<Filter>Source Files</Filter>
125125
</ClCompile>
126+
<ClCompile Include="vhci_vusb.c">
127+
<Filter>Source Files</Filter>
128+
</ClCompile>
126129
</ItemGroup>
127130
<ItemGroup>
128131
<None Include="custom_wpp.ini" />

driver/vhci_ude/vhci_dbg.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,4 @@ extern const char *dbg_urbr(purb_req_t urbr);
2323

2424
#ifndef DBG
2525
const char *dbg_usbd_status(USBD_STATUS status);
26-
#endif
26+
#endif

driver/vhci_ude/vhci_dev.h

+10-3
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ typedef struct _ctx_vusb
3434
*/
3535
BOOLEAN is_simple_ep_alloc;
3636
BOOLEAN invalid;
37+
/* reference count to prevent from removal while being used */
38+
ULONG refcnt;
39+
3740
// pending req which doesn't find an available urbr
3841
WDFREQUEST pending_req_read;
3942
// a partially transferred urbr
@@ -90,15 +93,19 @@ typedef struct _ctx_safe_vusb
9093
{
9194
pctx_vhci_t vhci;
9295
ULONG port;
93-
pctx_vusb_t vusb;
9496
} ctx_safe_vusb_t, *pctx_safe_vusb_t;
9597

9698
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(ctx_safe_vusb_t, TO_SAFE_VUSB)
9799

100+
#define TO_SAFE_VUSB_FROM_REQ(req) TO_SAFE_VUSB(WdfRequestGetFileObject(req))
101+
98102
#define VUSB_CREATING ((pctx_vusb_t)-1)
99-
#define VUSB_DELETING ((pctx_vusb_t)1)
100-
#define VUSB_IS_VALID(vusb) ((vusb) != NULL && (vusb) != VUSB_CREATING && (vusb) != VUSB_DELETING)
103+
#define VUSB_IS_VALID(vusb) ((vusb) != NULL && (vusb) != VUSB_CREATING && !(vusb)->invalid)
101104

102105
extern NTSTATUS plugout_vusb(pctx_vhci_t vhci, CHAR port);
103106

107+
extern pctx_vusb_t get_vusb(pctx_vhci_t vhci, ULONG port);
108+
extern pctx_vusb_t get_vusb_by_req(WDFREQUEST req);
109+
extern void put_vusb(pctx_vusb_t vusb);
110+
104111
EXTERN_C_END

driver/vhci_ude/vhci_hc.c

+2-20
Original file line numberDiff line numberDiff line change
@@ -61,31 +61,13 @@ create_fileobject(_In_ WDFDEVICE hdev, WDFREQUEST req, _In_ WDFFILEOBJECT fo)
6161
TRD(VHCI, "Enter");
6262

6363
svusb->vhci = vhci;
64-
svusb->vusb = NULL;
64+
svusb->port = (ULONG)-1;
6565

6666
WdfRequestComplete(req, STATUS_SUCCESS);
6767

6868
TRD(VHCI, "Leave");
6969
}
7070

71-
static VOID
72-
cleanup_fileobject(_In_ WDFFILEOBJECT fo)
73-
{
74-
pctx_safe_vusb_t svusb = TO_SAFE_VUSB(fo);
75-
76-
TRD(VHCI, "Enter");
77-
78-
/*
79-
* Not sure but the vusb maybe already be destroyed.
80-
* So after checked, proceed to plug out.
81-
*/
82-
if (svusb->vusb != NULL && svusb->vhci->vusbs[svusb->port] == svusb->vusb) {
83-
plugout_vusb(svusb->vhci, (CHAR)svusb->port);
84-
}
85-
86-
TRD(VHCI, "Leave");
87-
}
88-
8971
static PAGEABLE VOID
9072
setup_fileobject(PWDFDEVICE_INIT dinit)
9173
{
@@ -95,7 +77,7 @@ setup_fileobject(PWDFDEVICE_INIT dinit)
9577
PAGED_CODE();
9678

9779
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attrs, ctx_safe_vusb_t);
98-
WDF_FILEOBJECT_CONFIG_INIT(&conf, create_fileobject, NULL, cleanup_fileobject);
80+
WDF_FILEOBJECT_CONFIG_INIT(&conf, create_fileobject, NULL, NULL);
9981
WdfDeviceInitSetFileObjectConfig(dinit, &conf, &attrs);
10082
}
10183

driver/vhci_ude/vhci_ioctl.c

+24
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,27 @@ ioctl_plugout_vusb(WDFQUEUE queue, WDFREQUEST req, size_t inlen)
160160
return plugout_vusb(vhci, port);
161161
}
162162

163+
static NTSTATUS
164+
ioctl_shutdown_vusb(WDFQUEUE queue, WDFREQUEST req)
165+
{
166+
pctx_vhci_t vhci;
167+
pctx_vusb_t vusb;
168+
NTSTATUS status;
169+
170+
vusb = get_vusb_by_req(req);
171+
if (vusb == NULL) {
172+
/* already detached */
173+
return STATUS_SUCCESS;
174+
}
175+
176+
vhci = *TO_PVHCI(queue);
177+
178+
status = plugout_vusb(vhci, (CHAR)vusb->port);
179+
put_vusb(vusb);
180+
181+
return status;
182+
}
183+
163184
VOID
164185
io_device_control(_In_ WDFQUEUE queue, _In_ WDFREQUEST req,
165186
_In_ size_t outlen, _In_ size_t inlen, _In_ ULONG ioctl_code)
@@ -183,6 +204,9 @@ io_device_control(_In_ WDFQUEUE queue, _In_ WDFREQUEST req,
183204
case IOCTL_USBIP_VHCI_UNPLUG_HARDWARE:
184205
status = ioctl_plugout_vusb(queue, req, inlen);
185206
break;
207+
case IOCTL_USBIP_VHCI_SHUTDOWN_HARDWARE:
208+
status = ioctl_shutdown_vusb(queue, req);
209+
break;
186210
default:
187211
if (UdecxWdfDeviceTryHandleUserIoctl((*TO_PVHCI(queue))->hdev, req)) {
188212
TRD(IOCTL, "Leave: handled by Udecx");

driver/vhci_ude/vhci_plugin.c

+4-10
Original file line numberDiff line numberDiff line change
@@ -94,13 +94,15 @@ setup_vusb(UDECXUSBDEVICE ude_usbdev, pvhci_pluginfo_t pluginfo)
9494
}
9595

9696
vusb->devid = pluginfo->devid;
97+
vusb->port = pluginfo->port;
9798

9899
vusb->ude_usbdev = ude_usbdev;
99100
vusb->pending_req_read = NULL;
100101
vusb->urbr_sent_partial = NULL;
101102
vusb->len_sent_partial = 0;
102103
vusb->seq_num = 0;
103104
vusb->invalid = FALSE;
105+
vusb->refcnt = 0;
104106

105107
if (vusb->iSerial > 0 && pluginfo->wserial[0] != L'\0')
106108
vusb->wserial = libdrv_strdupW(pluginfo->wserial);
@@ -341,17 +343,9 @@ plugin_vusb(pctx_vhci_t vhci, WDFREQUEST req, pvhci_pluginfo_t pluginfo)
341343

342344
WdfSpinLockAcquire(vhci->spin_lock);
343345
if (vusb != NULL) {
344-
WDFFILEOBJECT fo = WdfRequestGetFileObject(req);
345-
if (fo != NULL) {
346-
pctx_safe_vusb_t svusb = TO_SAFE_VUSB(fo);
346+
pctx_safe_vusb_t svusb = TO_SAFE_VUSB_FROM_REQ(req);
347347

348-
svusb->vhci = vhci;
349-
svusb->port = pluginfo->port;
350-
svusb->vusb = vusb;
351-
}
352-
else {
353-
TRE(PLUGIN, "empty fileobject. setup failed");
354-
}
348+
svusb->port = pluginfo->port;
355349
status = STATUS_SUCCESS;
356350
}
357351
vhci->vusbs[pluginfo->port] = vusb;

driver/vhci_ude/vhci_plugout.c

+11-46
Original file line numberDiff line numberDiff line change
@@ -50,27 +50,19 @@ abort_all_pending_urbrs(pctx_vusb_t vusb)
5050
WdfSpinLockRelease(vusb->spin_lock);
5151
}
5252

53-
static NTSTATUS
53+
static void
5454
vusb_plugout(pctx_vusb_t vusb)
5555
{
56-
NTSTATUS status;
57-
5856
/*
5957
* invalidate first to prevent requests from an upper layer.
6058
* If requests are consistently fed into a vusb about to be plugged out,
61-
* a live deadlock may occur where vusb aborts pending urbs indefinately.
59+
* a live deadlock may occur where vusb aborts pending urbs indefinately.
6260
*/
6361
vusb->invalid = TRUE;
6462
abort_pending_req_read(vusb);
6563
abort_all_pending_urbrs(vusb);
6664

67-
status = UdecxUsbDevicePlugOutAndDelete(vusb->ude_usbdev);
68-
if (NT_ERROR(status)) {
69-
vusb->invalid = FALSE;
70-
TRD(PLUGIN, "failed to plug out: %!STATUS!", status);
71-
return status;
72-
}
73-
return STATUS_SUCCESS;
65+
TRD(PLUGIN, "plugged out: port: %d", vusb->port);
7466
}
7567

7668
static NTSTATUS
@@ -80,27 +72,16 @@ plugout_all_vusbs(pctx_vhci_t vhci)
8072

8173
TRD(PLUGIN, "plugging out all the devices!");
8274

83-
WdfSpinLockAcquire(vhci->spin_lock);
8475
for (i = 0; i < vhci->n_max_ports; i++) {
85-
NTSTATUS status;
86-
pctx_vusb_t vusb = vhci->vusbs[i];
76+
pctx_vusb_t vusb;
77+
78+
vusb = get_vusb(vhci, i);
8779
if (vusb == NULL)
8880
continue;
8981

90-
vhci->vusbs[i] = VUSB_DELETING;
91-
WdfSpinLockRelease(vhci->spin_lock);
92-
93-
status = vusb_plugout(vusb);
94-
95-
WdfSpinLockAcquire(vhci->spin_lock);
96-
if (NT_ERROR(status)) {
97-
vhci->vusbs[i] = vusb;
98-
WdfSpinLockRelease(vhci->spin_lock);
99-
return STATUS_UNSUCCESSFUL;
100-
}
101-
vhci->vusbs[i] = NULL;
82+
vusb_plugout(vusb);
83+
put_vusb(vusb);
10284
}
103-
WdfSpinLockRelease(vhci->spin_lock);
10485

10586
return STATUS_SUCCESS;
10687
}
@@ -109,36 +90,20 @@ NTSTATUS
10990
plugout_vusb(pctx_vhci_t vhci, CHAR port)
11091
{
11192
pctx_vusb_t vusb;
112-
NTSTATUS status;
11393

11494
if (port < 0)
11595
return plugout_all_vusbs(vhci);
11696

11797
TRD(IOCTL, "plugging out device: port: %u", port);
11898

119-
WdfSpinLockAcquire(vhci->spin_lock);
120-
121-
vusb = vhci->vusbs[port];
99+
vusb = get_vusb(vhci, port);
122100
if (vusb == NULL) {
123101
TRD(PLUGIN, "no matching vusb: port: %u", port);
124-
WdfSpinLockRelease(vhci->spin_lock);
125102
return STATUS_NO_SUCH_DEVICE;
126103
}
127104

128-
vhci->vusbs[port] = VUSB_DELETING;
129-
WdfSpinLockRelease(vhci->spin_lock);
130-
131-
status = vusb_plugout(vusb);
132-
133-
WdfSpinLockAcquire(vhci->spin_lock);
134-
if (NT_ERROR(status)) {
135-
vhci->vusbs[port] = vusb;
136-
WdfSpinLockRelease(vhci->spin_lock);
137-
return STATUS_UNSUCCESSFUL;
138-
}
139-
vhci->vusbs[port] = NULL;
140-
141-
WdfSpinLockRelease(vhci->spin_lock);
105+
vusb_plugout(vusb);
106+
put_vusb(vusb);
142107

143108
TRD(IOCTL, "completed to plug out: port: %u", port);
144109

driver/vhci_ude/vhci_read.c

+14-15
Original file line numberDiff line numberDiff line change
@@ -42,19 +42,19 @@ get_partial_urbr(pctx_vusb_t vusb)
4242
static VOID
4343
req_read_cancelled(WDFREQUEST req_read)
4444
{
45-
pctx_safe_vusb_t svusb;
4645
pctx_vusb_t vusb;
4746

4847
TRD(READ, "a pending read req cancelled");
4948

50-
svusb = TO_SAFE_VUSB(WdfRequestGetFileObject(req_read));
51-
vusb = svusb->vusb;
52-
53-
WdfSpinLockAcquire(vusb->spin_lock);
54-
if (vusb->pending_req_read == req_read) {
55-
vusb->pending_req_read = NULL;
49+
vusb = get_vusb_by_req(req_read);
50+
if (vusb != NULL) {
51+
WdfSpinLockAcquire(vusb->spin_lock);
52+
if (vusb->pending_req_read == req_read) {
53+
vusb->pending_req_read = NULL;
54+
}
55+
WdfSpinLockRelease(vusb->spin_lock);
56+
put_vusb(vusb);
5657
}
57-
WdfSpinLockRelease(vusb->spin_lock);
5858

5959
WdfRequestComplete(req_read, STATUS_CANCELLED);
6060
}
@@ -132,23 +132,22 @@ read_vusb(pctx_vusb_t vusb, WDFREQUEST req)
132132
VOID
133133
io_read(_In_ WDFQUEUE queue, _In_ WDFREQUEST req, _In_ size_t len)
134134
{
135-
pctx_safe_vusb_t svusb;
136135
pctx_vusb_t vusb;
137136
NTSTATUS status;
138137

139138
UNREFERENCED_PARAMETER(queue);
140139

141140
TRD(READ, "Enter: len: %u", (ULONG)len);
142141

143-
svusb = TO_SAFE_VUSB(WdfRequestGetFileObject(req));
144-
vusb = svusb->vusb;
145-
146-
if (vusb->invalid) {
147-
TRD(READ, "vusb disconnected: port: %u", vusb->port);
142+
vusb = get_vusb_by_req(req);
143+
if (vusb == NULL) {
144+
TRD(READ, "vusb disconnected: port: %u", TO_SAFE_VUSB_FROM_REQ(req)->port);
148145
status = STATUS_DEVICE_NOT_CONNECTED;
149146
}
150-
else
147+
else {
151148
status = read_vusb(vusb, req);
149+
put_vusb(vusb);
150+
}
152151

153152
if (status != STATUS_PENDING) {
154153
WdfRequestComplete(req, status);

driver/vhci_ude/vhci_vusb.c

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#include "vhci_driver.h"
2+
#include "vhci_vusb.tmh"
3+
4+
pctx_vusb_t
5+
get_vusb(pctx_vhci_t vhci, ULONG port)
6+
{
7+
pctx_vusb_t vusb;
8+
9+
WdfSpinLockAcquire(vhci->spin_lock);
10+
11+
vusb = vhci->vusbs[port];
12+
if (vusb == NULL || vusb->invalid) {
13+
WdfSpinLockRelease(vhci->spin_lock);
14+
return NULL;
15+
}
16+
vusb->refcnt++;
17+
18+
WdfSpinLockRelease(vhci->spin_lock);
19+
20+
return vusb;
21+
}
22+
23+
pctx_vusb_t
24+
get_vusb_by_req(WDFREQUEST req)
25+
{
26+
pctx_safe_vusb_t svusb;
27+
28+
svusb = TO_SAFE_VUSB_FROM_REQ(req);
29+
return get_vusb(svusb->vhci, svusb->port);
30+
}
31+
32+
void
33+
put_vusb(pctx_vusb_t vusb)
34+
{
35+
pctx_vhci_t vhci = vusb->vhci;
36+
37+
WdfSpinLockAcquire(vhci->spin_lock);
38+
39+
ASSERT(vusb->refcnt > 0);
40+
vusb->refcnt--;
41+
if (vusb->refcnt == 0 && vusb->invalid) {
42+
NTSTATUS status;
43+
44+
vhci->vusbs[vusb->port] = NULL;
45+
WdfSpinLockRelease(vhci->spin_lock);
46+
47+
status = UdecxUsbDevicePlugOutAndDelete(vusb->ude_usbdev);
48+
if (NT_ERROR(status)) {
49+
TRD(PLUGIN, "failed to plug out: %!STATUS!", status);
50+
}
51+
}
52+
else {
53+
WdfSpinLockRelease(vhci->spin_lock);
54+
}
55+
}

0 commit comments

Comments
 (0)