Skip to content

Commit b5dc887

Browse files
committed
transfer: create locking mechanism without using spinlocks
transfer: make locking multi-core safe transfer: fix problems with sequence locking
1 parent 6ea0b67 commit b5dc887

3 files changed

Lines changed: 69 additions & 47 deletions

File tree

libusb/src/driver/libusb_driver.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -320,9 +320,6 @@ NTSTATUS DDKAPI add_device(DRIVER_OBJECT *driver_object,
320320
clear_pipe_info(dev);
321321

322322
remove_lock_initialize(dev);
323-
324-
/* Initialize the pending lock */
325-
KeInitializeSpinLock(&dev->pending_lock);
326323

327324
if (dev->device_interface_in_use)
328325
{
@@ -782,6 +779,7 @@ void remove_lock_release_and_wait(libusb_device_t *dev)
782779
FALSE, NULL);
783780
}
784781

782+
785783
USB_INTERFACE_DESCRIPTOR *
786784
find_interface_desc(USB_CONFIGURATION_DESCRIPTOR *config_desc,
787785
unsigned int size, int interface_number, int altsetting)

libusb/src/driver/libusb_driver.h

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,6 @@ typedef struct
184184
DEVICE_OBJECT *physical_device_object;
185185
DEVICE_OBJECT *next_stack_device;
186186
DEVICE_OBJECT *target_device;
187-
188187
libusb_remove_lock_t remove_lock;
189188
bool_t is_filter;
190189
bool_t is_started;
@@ -212,13 +211,11 @@ typedef struct
212211
int control_read_timeout;
213212
int control_write_timeout;
214213

215-
/* Mutex that makes sure the pending counters are updated securely */
216-
KSPIN_LOCK pending_lock;
217-
218214
/* Keep track of head pending request sequences on all endpoints
219215
* This housekeeping is here to make sure we do not sumbit read/writes out of order
220216
*/
221-
int pending_sequence[LIBUSB_MAX_ENDPOINT_NO];
217+
LONG pending_sequence[LIBUSB_MAX_ENDPOINT_NO];
218+
LONG pending_busy[LIBUSB_MAX_ENDPOINT_NO];
222219
} libusb_device_t, DEVICE_EXTENSION, *PDEVICE_EXTENSION;
223220

224221

libusb/src/driver/transfer.c

Lines changed: 66 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ typedef struct
2424
{
2525
URB *urb;
2626
int address;
27-
int sequence;
27+
LONG sequence;
2828
int transferFlags;
2929
int isoLatency;
3030
int totalLength;
@@ -60,19 +60,6 @@ void set_urb_transfer_flags(libusb_device_t* dev,
6060
PURB subUrb,
6161
int transfer_flags,
6262
int isoLatency);
63-
64-
static KIRQL pending_lock_acquire(libusb_device_t* dev)
65-
{
66-
KIRQL old;
67-
KeAcquireSpinLock(&dev->pending_lock, &old);
68-
return old;
69-
}
70-
71-
static void pending_lock_release(libusb_device_t* dev, KIRQL old)
72-
{
73-
KeReleaseSpinLock(&dev->pending_lock, old);
74-
}
75-
7663
static NTSTATUS transfer_next(libusb_device_t* dev,
7764
IN PIRP irp,
7865
context_t* context)
@@ -113,11 +100,12 @@ NTSTATUS transfer(libusb_device_t* dev,
113100
IN int totalLength,
114101
IN int maxTransferSize)
115102
{
116-
context_t *context;
103+
context_t *context = NULL;
117104
NTSTATUS status = STATUS_SUCCESS;
118105
int sequenceID = InterlockedIncrement(&sequence);
119106
const char* dispTransfer = GetPipeDisplayName(endpoint);
120107
int first_size;
108+
LONG pending_busy_taken = FALSE, pending_busy = FALSE;
121109

122110
// TODO: reset pipe flag
123111
// status = reset_endpoint(dev,endpoint->address, LIBUSB_DEFAULT_TIMEOUT);
@@ -135,12 +123,25 @@ NTSTATUS transfer(libusb_device_t* dev,
135123
USBMSG("[%s #%d] EP%02Xh length=%d, packetSize=%d, maxTransferSize=%d\n",
136124
dispTransfer, sequenceID, endpoint->address, totalLength, packetSize, maxTransferSize);
137125
}
138-
context = allocate_pool(sizeof(context_t));
139126

127+
/* Check if another transfer is on-going on same endpoint */
128+
pending_busy = InterlockedCompareExchange(&dev->pending_busy[endpoint->address], 1, 0);
129+
if(pending_busy)
130+
{
131+
USBMSG("sequence %d send aborted due to pending conflict\n", sequenceID);
132+
goto transfer_free;
133+
}
134+
pending_busy_taken = TRUE;
135+
136+
/* Save the pending sequence, so that transfer_complete() for older requests do not
137+
order new transfers after this one */
138+
InterlockedExchange(&dev->pending_sequence[endpoint->address], sequenceID);
139+
140+
context = allocate_pool(sizeof(context_t));
140141
if (!context)
141142
{
142-
remove_lock_release(dev);
143-
return complete_irp(irp, STATUS_NO_MEMORY, 0);
143+
status = STATUS_NO_MEMORY;
144+
goto transfer_free;
144145
}
145146

146147
context->isoLatency = isoLatency;
@@ -160,37 +161,43 @@ NTSTATUS transfer(libusb_device_t* dev,
160161
endpoint, packetSize, mdlAddress, first_size);
161162
if (!NT_SUCCESS(status))
162163
{
163-
ExFreePool(context);
164-
remove_lock_release(dev);
165-
return complete_irp(irp, status, 0);
164+
goto transfer_free;
166165
}
167166

168-
/* Grab the pending lock. This makes sure a DPC does not arrive
169-
while we are sending the next sequence */
170-
KIRQL old = pending_lock_acquire(dev);
171-
dev->pending_sequence[context->address] = sequenceID;
172-
173-
status = transfer_next(dev, irp, context);
167+
status = transfer_next(dev, irp, context);
174168
if (!NT_SUCCESS(status))
175169
{
176-
pending_lock_release(dev, old);
177-
178-
ExFreePool(context->urb);
179-
ExFreePool(context);
180-
remove_lock_release(dev);
181-
return complete_irp(irp, status, 0);
170+
goto transfer_free;
182171
}
183172

184-
pending_lock_release(dev, old);
173+
InterlockedExchange(&dev->pending_busy[endpoint->address], 0);
185174
return status;
175+
176+
transfer_free:
177+
if(pending_busy_taken)
178+
{
179+
InterlockedExchange(&dev->pending_busy[endpoint->address], 0);
180+
}
181+
if(context)
182+
{
183+
if(context->urb)
184+
{
185+
ExFreePool(context->urb);
186+
}
187+
ExFreePool(context);
188+
}
189+
remove_lock_release(dev);
190+
return complete_irp(irp, status, 0);
186191
}
192+
187193
NTSTATUS DDKAPI transfer_complete(DEVICE_OBJECT *device_object, IRP *irp,
188194
void *context)
189195
{
190196
NTSTATUS status = STATUS_SUCCESS;
191197
context_t *c = (context_t *)context;
192198
int transmitted = 0;
193199
libusb_device_t *dev = device_object->DeviceExtension;
200+
LONG pending_busy_taken = FALSE, pending_busy = FALSE;
194201

195202
if (irp->PendingReturned)
196203
{
@@ -234,22 +241,37 @@ NTSTATUS DDKAPI transfer_complete(DEVICE_OBJECT *device_object, IRP *irp,
234241
/* Update transferred size */
235242
c->information += transmitted;
236243

237-
USBERR("sequence %d: pending seq %i\n", c->sequence, dev->pending_sequence[c->address]);
238-
239244
/* If this is a success, and there is more data to be transferred, then lets go again
240245
*
241-
* Note 1: We do not do this if other requests are already pending on the endpoint,
246+
* Note 1: We do not do this if a newer request is already pending on the endpoint,
242247
* as this could change the order of the arrival of data
243248
*/
244249
if(NT_SUCCESS(irp->IoStatus.Status)
245-
&& (dev->pending_sequence[c->address] == c->sequence)
246250
&& USBD_SUCCESS(c->urb->UrbHeader.Status)
247251
&& (c->urb->UrbHeader.Function == URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER)
248252
&& !(transmitted % c->maximum_packet_size)
249253
&& c->totalLength)
250254
{
255+
PUCHAR virtualAddress;
251256
int next_size = (c->totalLength > c->maxTransferSize) ? c->maxTransferSize : c->totalLength;
252-
PUCHAR virtualAddress = (PUCHAR)MmGetMdlVirtualAddress(c->mdlAddress);
257+
258+
/* Check if another transfer is on-going on same endpoint */
259+
pending_busy = InterlockedCompareExchange(&dev->pending_busy[c->address], 1, 0);
260+
if(pending_busy)
261+
{
262+
USBMSG("sequence %d resend aborted due to pending conflict\n", c->sequence);
263+
goto transfer_free;
264+
}
265+
pending_busy_taken = TRUE;
266+
267+
/* Check if a newer sequence is pending */
268+
if(InterlockedAdd(&dev->pending_sequence[c->address], 0) != c->sequence)
269+
{
270+
USBMSG("sequence %d resend aborted due to newer pending\n", c->sequence);
271+
goto transfer_free;
272+
}
273+
274+
virtualAddress = (PUCHAR)MmGetMdlVirtualAddress(c->mdlAddress);
253275
if(!virtualAddress)
254276
{
255277
USBERR("[#%d] MmGetMdlVirtualAddress failed\n", c->sequence);
@@ -281,11 +303,16 @@ NTSTATUS DDKAPI transfer_complete(DEVICE_OBJECT *device_object, IRP *irp,
281303
goto transfer_free;
282304
}
283305

306+
InterlockedExchange(&dev->pending_busy[c->address], 0);
284307
return STATUS_MORE_PROCESSING_REQUIRED;
285308
}
286309

287310
transfer_free:
288311

312+
if(pending_busy_taken)
313+
{
314+
InterlockedExchange(&dev->pending_busy[c->address], 0);
315+
}
289316
irp->IoStatus.Information = c->information;
290317
if(c->subMdl)
291318
{

0 commit comments

Comments
 (0)