Skip to content

Commit

Permalink
vhci, notify a usbip server of urb cancellation
Browse files Browse the repository at this point in the history
Old vhci did not notify a usbip server with UNLINK command when
a submitted urb is cancelled. This might cause a upper driver to
freeze. When MS lifeCam was attached as a usb client, Camera
app had terminated abruptly or gived a error. This commit will
remedy this problem.
  • Loading branch information
cezanne committed Feb 2, 2019
1 parent a32b206 commit 10d26c6
Show file tree
Hide file tree
Showing 7 changed files with 156 additions and 91 deletions.
133 changes: 68 additions & 65 deletions driver/vhci/usbreq.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ dbg_urbr(struct urb_req *urbr)

if (urbr == NULL)
return "[null]";
dbg_snprintf(buf, 128, "[seq:%d]", urbr->seq_num);
dbg_snprintf(buf, 128, "[seq:%u]", urbr->seq_num);
return buf;
}

Expand Down Expand Up @@ -70,36 +70,66 @@ find_pending_urbr(pusbip_vpdo_dev_t vpdo)
return urbr;
}

static void
remove_cancelled_urbr(pusbip_vpdo_dev_t vpdo, PIRP irp)
static struct urb_req *
find_urbr_with_irp(pusbip_vpdo_dev_t vpdo, PIRP irp)
{
KIRQL oldirql = irp->CancelIrql;
PLIST_ENTRY le;

KeAcquireSpinLockAtDpcLevel(&vpdo->lock_urbr);

for (le = vpdo->head_urbr.Flink; le != &vpdo->head_urbr; le = le->Flink) {
struct urb_req *urbr;

urbr = CONTAINING_RECORD(le, struct urb_req, list_all);
if (urbr->irp == irp) {
RemoveEntryList(le);
RemoveEntryList(&urbr->list_state);
if (vpdo->urbr_sent_partial == urbr) {
vpdo->urbr_sent_partial = NULL;
vpdo->len_sent_partial = 0;
}
KeReleaseSpinLock(&vpdo->lock_urbr, oldirql);
if (urbr->irp == irp)
return urbr;
}

return NULL;
}

static void
submit_urbr_unlink(pusbip_vpdo_dev_t vpdo, unsigned long seq_num_unlink)
{
struct urb_req *urbr_unlink;

urbr_unlink = create_urbr(vpdo, NULL, seq_num_unlink);
if (urbr_unlink != NULL) {
NTSTATUS status = submit_urbr(vpdo, urbr_unlink);
if (NT_ERROR(status)) {
DBGI(DBG_GENERAL, "failed to submit unlink urb: %s\n", dbg_urbr(urbr_unlink));
ExFreeToNPagedLookasideList(&g_lookaside, urbr_unlink);
}
}
}

static void
remove_cancelled_urbr(pusbip_vpdo_dev_t vpdo, PIRP irp)
{
struct urb_req *urbr;
KIRQL oldirql = irp->CancelIrql;

DBGI(DBG_GENERAL, "urb cancelled: %s\n", dbg_urbr(urbr));
ExFreeToNPagedLookasideList(&g_lookaside, urbr);
return;
KeAcquireSpinLockAtDpcLevel(&vpdo->lock_urbr);

urbr = find_urbr_with_irp(vpdo, irp);
if (urbr != NULL) {
RemoveEntryList(&urbr->list_state);
RemoveEntryList(&urbr->list_all);
if (vpdo->urbr_sent_partial == urbr) {
vpdo->urbr_sent_partial = NULL;
vpdo->len_sent_partial = 0;
}
}
else {
DBGW(DBG_URB, "no matching urbr\n");
}

KeReleaseSpinLock(&vpdo->lock_urbr, oldirql);

DBGW(DBG_GENERAL, "no matching urbr\n");
if (urbr != NULL) {
submit_urbr_unlink(vpdo, urbr->seq_num);

DBGI(DBG_GENERAL, "cancelled urb destroyed: %s\n", dbg_urbr(urbr));
ExFreeToNPagedLookasideList(&g_lookaside, urbr);
}
}

static void
Expand All @@ -117,8 +147,8 @@ cancel_urbr(PDEVICE_OBJECT devobj, PIRP irp)
IoReleaseCancelSpinLock(irp->CancelIrql);
}

static struct urb_req *
create_urbr(pusbip_vpdo_dev_t vpdo, PIRP irp)
struct urb_req *
create_urbr(pusbip_vpdo_dev_t vpdo, PIRP irp, unsigned long seq_num_unlink)
{
struct urb_req *urbr;

Expand All @@ -130,51 +160,28 @@ create_urbr(pusbip_vpdo_dev_t vpdo, PIRP irp)
RtlZeroMemory(urbr, sizeof(*urbr));
urbr->vpdo = vpdo;
urbr->irp = irp;
urbr->seq_num_unlink = seq_num_unlink;
return urbr;
}

static BOOLEAN
insert_pending_or_sent_urbr(pusbip_vpdo_dev_t vpdo, struct urb_req *urbr, BOOLEAN is_pending)
{
PIRP irp = urbr->irp;

IoSetCancelRoutine(irp, cancel_urbr);
if (irp->Cancel) {
IoSetCancelRoutine(irp, NULL);
return FALSE;
}
else {
IoMarkIrpPending(irp);
if (is_pending)
InsertTailList(&vpdo->head_urbr_pending, &urbr->list_state);
else
InsertTailList(&vpdo->head_urbr_sent, &urbr->list_state);
}
return TRUE;
}

NTSTATUS
submit_urbr(pusbip_vpdo_dev_t vpdo, PIRP irp)
submit_urbr(pusbip_vpdo_dev_t vpdo, struct urb_req *urbr)
{
struct urb_req *urbr;
KIRQL oldirql;
PIRP read_irp;
NTSTATUS status = STATUS_PENDING;

if ((urbr = create_urbr(vpdo, irp)) == NULL)
return STATUS_INSUFFICIENT_RESOURCES;

KeAcquireSpinLock(&vpdo->lock_urbr, &oldirql);

if (vpdo->urbr_sent_partial || vpdo->pending_read_irp == NULL) {
if (!insert_pending_or_sent_urbr(vpdo, urbr, TRUE)) {
KeReleaseSpinLock(&vpdo->lock_urbr, oldirql);
ExFreeToNPagedLookasideList(&g_lookaside, urbr);
DBGI(DBG_URB, "submit_urbr: urb cancelled\n");
return STATUS_CANCELLED;
if (urbr->irp != NULL) {
IoSetCancelRoutine(urbr->irp, cancel_urbr);
IoMarkIrpPending(urbr->irp);
}
InsertTailList(&vpdo->head_urbr_pending, &urbr->list_state);
InsertTailList(&vpdo->head_urbr, &urbr->list_all);
KeReleaseSpinLock(&vpdo->lock_urbr, oldirql);

DBGI(DBG_URB, "submit_urbr: urb pending\n");
return STATUS_PENDING;
}
Expand All @@ -191,31 +198,27 @@ submit_urbr(pusbip_vpdo_dev_t vpdo, PIRP irp)
KeAcquireSpinLock(&vpdo->lock_urbr, &oldirql);

if (status == STATUS_SUCCESS) {
if (urbr->irp != NULL) {
IoSetCancelRoutine(urbr->irp, cancel_urbr);
IoMarkIrpPending(urbr->irp);
}
if (vpdo->len_sent_partial == 0) {
vpdo->urbr_sent_partial = NULL;
if (!insert_pending_or_sent_urbr(vpdo, urbr, FALSE))
status = STATUS_CANCELLED;
InsertTailList(&vpdo->head_urbr_sent, &urbr->list_state);
}

if (status == STATUS_SUCCESS) {
InsertTailList(&vpdo->head_urbr, &urbr->list_all);
vpdo->pending_read_irp = NULL;
KeReleaseSpinLock(&vpdo->lock_urbr, oldirql);
InsertTailList(&vpdo->head_urbr, &urbr->list_all);
vpdo->pending_read_irp = NULL;
KeReleaseSpinLock(&vpdo->lock_urbr, oldirql);

read_irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(read_irp, IO_NO_INCREMENT);
status = STATUS_PENDING;
}
else {
KeReleaseSpinLock(&vpdo->lock_urbr, oldirql);
ExFreeToNPagedLookasideList(&g_lookaside, urbr);
}
read_irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(read_irp, IO_NO_INCREMENT);
status = STATUS_PENDING;
}
else {
vpdo->urbr_sent_partial = NULL;
KeReleaseSpinLock(&vpdo->lock_urbr, oldirql);

ExFreeToNPagedLookasideList(&g_lookaside, urbr);
status = STATUS_INVALID_PARAMETER;
}
DBGI(DBG_URB, "submit_urbr: urb requested: status:%s\n", dbg_ntstatus(status));
Expand Down
8 changes: 7 additions & 1 deletion driver/vhci/usbreq.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,16 @@ struct urb_req {
pusbip_vpdo_dev_t vpdo;
PIRP irp;
KEVENT *event;
unsigned long seq_num;
unsigned long seq_num, seq_num_unlink;
LIST_ENTRY list_all;
LIST_ENTRY list_state;
};

extern void
build_setup_packet(usb_cspkt_t *csp, unsigned char direct_in, unsigned char type, unsigned char recip, unsigned char request);

extern NTSTATUS
submit_urbr(pusbip_vpdo_dev_t vpdo, struct urb_req *urbr);

extern struct urb_req *
create_urbr(pusbip_vpdo_dev_t vpdo, PIRP irp, unsigned long seq_num_unlink);
22 changes: 17 additions & 5 deletions driver/vhci/vhci_ioctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@
#include "vhci_pnp.h"
#include "usbip_vhci_api.h"

extern NTSTATUS
submit_urbr(pusbip_vpdo_dev_t vpdo, PIRP irp);

extern PAGEABLE NTSTATUS
vhci_plugin_dev(ioctl_usbip_vhci_plugin *plugin, pusbip_vhub_dev_t vhub, PFILE_OBJECT fo);

Expand Down Expand Up @@ -49,6 +46,21 @@ process_urb_get_frame(pusbip_vpdo_dev_t vpdo, PURB urb)
return STATUS_SUCCESS;
}

static NTSTATUS
submit_urbr_irp(pusbip_vpdo_dev_t vpdo, PIRP irp)
{
struct urb_req *urbr;
NTSTATUS status;

urbr = create_urbr(vpdo, irp, 0);
if (urbr == NULL)
return STATUS_INSUFFICIENT_RESOURCES;
status = submit_urbr(vpdo, urbr);
if (NT_ERROR(status))
ExFreeToNPagedLookasideList(&g_lookaside, urbr);
return status;
}

static NTSTATUS
process_irp_urb_req(pusbip_vpdo_dev_t vpdo, PIRP irp, PURB urb)
{
Expand Down Expand Up @@ -80,7 +92,7 @@ process_irp_urb_req(pusbip_vpdo_dev_t vpdo, PIRP irp, PURB urb)
case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE:
case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER:
case URB_FUNCTION_SELECT_INTERFACE:
return submit_urbr(vpdo, irp);
return submit_urbr_irp(vpdo, irp);
default:
DBGW(DBG_IOCTL, "process_irp_urb_req: unhandled function: %s: len: %d\n",
dbg_urbfunc(urb->UrbHeader.Function), urb->UrbHeader.Length);
Expand Down Expand Up @@ -141,7 +153,7 @@ vhci_internal_ioctl(__in PDEVICE_OBJECT devobj, __in PIRP Irp)
*(unsigned long *)irpStack->Parameters.Others.Argument1 = USBD_PORT_ENABLED | USBD_PORT_CONNECTED;
break;
case IOCTL_INTERNAL_USB_RESET_PORT:
status = submit_urbr(vpdo, Irp);
status = submit_urbr_irp(vpdo, Irp);
break;
case IOCTL_INTERNAL_USB_GET_TOPOLOGY_ADDRESS:
status = setup_topology_address(vpdo, irpStack);
Expand Down
12 changes: 7 additions & 5 deletions driver/vhci/vhci_pnp.c
Original file line number Diff line number Diff line change
Expand Up @@ -671,11 +671,13 @@ complete_pending_irp(pusbip_vpdo_dev_t vpdo)
}

ExFreeToNPagedLookasideList(&g_lookaside, urbr);
irp->IoStatus.Status = STATUS_DEVICE_NOT_CONNECTED;
IoSetCancelRoutine(irp, NULL);
KeRaiseIrql(DISPATCH_LEVEL, &oldirql2);
IoCompleteRequest (irp, IO_NO_INCREMENT);
KeLowerIrql(oldirql2);
if (irp != NULL) {
irp->IoStatus.Status = STATUS_DEVICE_NOT_CONNECTED;
IoSetCancelRoutine(irp, NULL);
KeRaiseIrql(DISPATCH_LEVEL, &oldirql2);
IoCompleteRequest(irp, IO_NO_INCREMENT);
KeLowerIrql(oldirql2);
}
count++;
} while (1);
}
Expand Down
11 changes: 11 additions & 0 deletions driver/vhci/vhci_proto.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,14 @@ set_cmd_submit_usbip_header(struct usbip_header *h, unsigned long seqnum, unsign
h->u.cmd_submit.number_of_packets = 0;
h->u.cmd_submit.interval = PIPE2INTERVAL(pipe);
}

void
set_cmd_unlink_usbip_header(struct usbip_header *h, unsigned long seqnum, unsigned int devid, unsigned long seqnum_unlink)
{
h->base.command = USBIP_CMD_UNLINK;
h->base.seqnum = seqnum;
h->base.devid = devid;
h->base.direction = USBIP_DIR_OUT;
h->base.ep = 0;
h->u.cmd_unlink.seqnum = seqnum_unlink;
}
30 changes: 27 additions & 3 deletions driver/vhci/vhci_read.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ extern void
set_cmd_submit_usbip_header(struct usbip_header *h, unsigned long seqnum, unsigned int devid,
unsigned int direct, USBD_PIPE_HANDLE pipe, unsigned int flags, unsigned int len);

void
set_cmd_unlink_usbip_header(struct usbip_header *h, unsigned long seqnum, unsigned int devid, unsigned long seqnum_unlink);

static struct usbip_header *
get_usbip_hdr_from_read_irp(PIRP irp)
{
Expand Down Expand Up @@ -548,6 +551,21 @@ store_urbr_partial(PIRP irp, struct urb_req *urbr)
return status;
}

static NTSTATUS
store_cancelled_urbr(PIRP irp, struct urb_req *urbr)
{
struct usbip_header *hdr;

hdr = get_usbip_hdr_from_read_irp(irp);
if (hdr == NULL)
return STATUS_INVALID_PARAMETER;

set_cmd_unlink_usbip_header(hdr, urbr->seq_num, urbr->vpdo->devid, urbr->seq_num_unlink);

irp->IoStatus.Information = sizeof(struct usbip_header);
return STATUS_SUCCESS;
}

NTSTATUS
store_urbr(PIRP irp, struct urb_req *urbr)
{
Expand All @@ -557,6 +575,10 @@ store_urbr(PIRP irp, struct urb_req *urbr)

DBGI(DBG_READ, "store_urbr: urbr: %s\n", dbg_urbr(urbr));

if (urbr->irp == NULL) {
return store_cancelled_urbr(irp, urbr);
}

irpstack = IoGetCurrentIrpStackLocation(urbr->irp);
ioctl_code = irpstack->Parameters.DeviceIoControl.IoControlCode;
switch (ioctl_code) {
Expand Down Expand Up @@ -617,9 +639,11 @@ process_read_irp(pusbip_vpdo_dev_t vpdo, PIRP read_irp)
RemoveEntryList(&urbr->list_all);
KeReleaseSpinLock(&vpdo->lock_urbr, oldirql);

IoSetCancelRoutine(urbr->irp, NULL);
urbr->irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
IoCompleteRequest(urbr->irp, IO_NO_INCREMENT);
if (urbr->irp != NULL) {
IoSetCancelRoutine(urbr->irp, NULL);
urbr->irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
IoCompleteRequest(urbr->irp, IO_NO_INCREMENT);
}
ExFreeToNPagedLookasideList(&g_lookaside, urbr);
}
else {
Expand Down
Loading

0 comments on commit 10d26c6

Please sign in to comment.