Ejemplo n.º 1
0
NTSTATUS 
ssh_interceptor_iodevice_dispatch_cleanup(PDEVICE_OBJECT device,
                                          PIRP irp)
{
  SshInterceptorIoDevice io_dev = SSH_NTDEV_TO_SSHDEV(device);
  PLIST_ENTRY entry;


  SSH_DEBUG(SSH_D_MIDSTART,
            ("ssh_interceptor_iodevice_dispatch_cleanup(irp = 0x%p)", irp));

  /* Cancel all pending IRPs */
  while ((entry = NdisInterlockedRemoveHeadList(&io_dev->read_queue,
                                       &io_dev->read_queue_lock)) != NULL)
    {
      PIRP cancelled_irp;

      cancelled_irp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry);
      cancelled_irp->CancelRoutine = NULL;
      cancelled_irp->IoStatus.Status = STATUS_CANCELLED;
      cancelled_irp->IoStatus.Information = 0;
      IoCompleteRequest(cancelled_irp, IO_NO_INCREMENT);

      SSH_DEBUG(SSH_D_NICETOKNOW,
                ("IoCompleteRequest(irp = 0x%p, status = STATUS_CANCELLED)",
                cancelled_irp));
    };

  irp->IoStatus.Status = STATUS_SUCCESS;
  irp->IoStatus.Information = 0;
  IoCompleteRequest(irp, IO_NO_INCREMENT);
  SSH_DEBUG(SSH_D_NICETOKNOW, ("IoCompleteRequest(irp = 0x%p)", irp));

  return STATUS_SUCCESS;
}
Ejemplo n.º 2
0
__inline SshDeviceBuffer
ssh_iodevice_buffer_alloc(SshInterceptorIoDevice io_dev,
                          Boolean reliable)
{
  SshDeviceBuffer buf = NULL;
  PLIST_ENTRY entry;

  /* 1. Try to get a SshDeviceBuffer from a free list */
  entry = NdisInterlockedRemoveHeadList(&io_dev->free_list,
                                        &io_dev->free_list_lock);
  if (entry)
    buf = CONTAINING_RECORD(entry, SshDeviceBufferStruct, link);

  /* 2. If failed and this is a reliable message, try to replace
     an existing unreliable one */
  if ((buf == NULL) && (reliable))
    {
      NdisAcquireSpinLock(&io_dev->output_queue_lock);
      if (!IsListEmpty(&io_dev->unreliable_output_queue))
        {
          /* We found an existing unreliable message */
          entry = RemoveHeadList(&io_dev->unreliable_output_queue);

          /* We must remove the entry from output_queue too */
          buf = CONTAINING_RECORD(entry, SshDeviceBufferStruct,
                                  unreliable_list_link);

          SSH_ASSERT(buf != io_dev->current_read_buf);

          /* This removes the entry from output_queue */
          RemoveEntryList(&(buf->link));
        }
      NdisReleaseSpinLock(&io_dev->output_queue_lock);

      /* If found, we must delete the old message */
      if (buf != NULL)
        ssh_free(buf->addr);
    }

  /* 3. If still failed, try to allocate memory for a new
     SshDeviceBuffer */
  if ((buf == NULL) && (reliable))
    {
      buf = ssh_malloc(sizeof(*buf));
      if (buf)
        {
          /* This buffer will be deleted after use */
          buf->pre_allocated = 0;
        }
    }

  return buf;
}
Ejemplo n.º 3
0
_Must_inspect_result_
PTCB
TXGetNextTcbToSend(
    _In_  PMP_ADAPTER      Adapter)
/*++

Routine Description:

    Returns the next TCB queued on the send list, or NULL if the list was empty.

    Runs at IRQL <= DISPATCH_LEVEL.

Arguments:

    Adapter                     Pointer to our adapter

Return Value:

    NULL if there was no TCB queued.
    Else, a pointer to the TCB that was popped off the top of the BusyTcbList.

--*/
{
    PTCB Tcb;
    PLIST_ENTRY pTcbEntry = NdisInterlockedRemoveHeadList(
            &Adapter->BusyTcbList,
            &Adapter->BusyTcbListLock);

    if (! pTcbEntry)
    {
        // End of list -- no more items to receive.
        return NULL;
    }

    Tcb = CONTAINING_RECORD(pTcbEntry, TCB, TcbLink);

    ASSERT(Tcb);
    ASSERT(Tcb->NetBuffer);

    return Tcb;
}
Ejemplo n.º 4
0
static void 
ssh_interceptor_iodevice_do_reads(SshInterceptorIoDevice io_dev)
{
  SshDeviceBuffer buffer;
  PIO_STACK_LOCATION irp_stack = NULL;
  char *dest = NULL;
  char *base_addr = NULL;
  PIRP irp = NULL;

  NdisAcquireSpinLock(&io_dev->output_queue_lock);
  buffer = io_dev->current_read_buf;
  NdisReleaseSpinLock(&io_dev->output_queue_lock);

  /* Complete as many queued output buffers and pending reads as possible */
  while (TRUE)
    {
      PLIST_ENTRY entry;
      unsigned bytes_copied;

      /* Try to find some data to write */
      if (buffer == NULL)
        {
          NdisAcquireSpinLock(&io_dev->output_queue_lock);
          if (!IsListEmpty(&io_dev->output_queue))
            {
              entry = RemoveHeadList(&io_dev->output_queue);
              buffer = CONTAINING_RECORD(entry, SshDeviceBufferStruct, link);
              /* If this is an unreliable message, it must also be removed
                 from the unreliable_output_queue! */
              if (buffer->reliable == 0)
                RemoveEntryList(&buffer->unreliable_list_link);
            }
          io_dev->current_read_buf = buffer;
          NdisReleaseSpinLock(&io_dev->output_queue_lock);

          /* Exit the loop if no data was available in output queue */
          if (buffer == NULL)
            goto complete;  
        }

      /* Try to find queued read IRP */
      if (irp == NULL)
        {
          entry = NdisInterlockedRemoveHeadList(&io_dev->read_queue,
                                                &io_dev->read_queue_lock);
          if (entry)
            {
              irp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry);
              irp_stack = IoGetCurrentIrpStackLocation(irp);

              /* There is no need to check for canceled status, because
                 cancelation is protected by the same lock as the queue
                 itself. We may actually pop off an IRP for which cancel
                 has already been issued, but we can complete it as usual. */
#pragma warning(disable: 4311 4312)
              IoSetCancelRoutine(irp, NULL);
#pragma warning(default: 4311 4312)
              irp->IoStatus.Information = 0;
              irp->IoStatus.Status = STATUS_SUCCESS;

              base_addr = ssh_iodevice_map_buffer(irp->MdlAddress);
              if (base_addr == NULL)
                {
                  /* Mapping of user-mode buffer could fail if the system is
                     very low on resources. */
                  IoCompleteRequest(irp, IO_NETWORK_INCREMENT);
                  irp = NULL;
                  continue;
                }

              dest = base_addr;
            }
          else
            {
              /* No read IRPs available, exit the loop */
              goto complete;
            }
        }

      /* We copy either the whole buffer or part of it if there isn't enough
         space left in the currently processed read IRP. */
      bytes_copied = buffer->len;
      if (irp->IoStatus.Information + bytes_copied >
          irp_stack->Parameters.Read.Length)
        {
          bytes_copied = irp_stack->Parameters.Read.Length -
                         (unsigned int)irp->IoStatus.Information;
        }

      NdisMoveMemory(dest, buffer->addr + buffer->offset, bytes_copied);

      buffer->offset += bytes_copied;
      buffer->len -= bytes_copied;
      if (buffer->len == 0)
        {
          NdisAcquireSpinLock(&io_dev->output_queue_lock);
          io_dev->current_read_buf = NULL;
          NdisReleaseSpinLock(&io_dev->output_queue_lock);

          ssh_iodevice_buffer_free(io_dev, buffer); 

          buffer = NULL;
        }

      irp->IoStatus.Information += bytes_copied;
      dest += bytes_copied;

      /* If the IRP is now "full", complete the request */
      if (irp->IoStatus.Information == irp_stack->Parameters.Read.Length)
        {
          ssh_iodevice_unmap_buffer(base_addr, irp->MdlAddress);
          IoCompleteRequest(irp, IO_NETWORK_INCREMENT);
          irp = NULL;
          base_addr = NULL;
        }
    }

complete:

  /* We should also complete the partially filled IRP, if any */
  if (irp)
    {
      ssh_iodevice_unmap_buffer(base_addr, irp->MdlAddress);
      IoCompleteRequest(irp, IO_NETWORK_INCREMENT);
    }
}
Ejemplo n.º 5
0
VOID 
ssh_driver_delayed_send_thread(SshNdisIMInterceptor interceptor)
{
  SshNdisIMAdapter adapter_lookup_table[32];
  SshNdisIMAdapter adapter;
  PLIST_ENTRY entry;
  SSH_IRQL old_irql;
  SshUInt32 i;
  SshUInt32 packets_sent;
  SshUInt32 adapter_cnt;

  SSH_ASSERT(interceptor != NULL);

  if (interceptor->adapter_cnt == 0)
    return;

  SSH_RAISE_IRQL(SSH_APC_LEVEL, &old_irql);

  ssh_kernel_rw_mutex_lock_read(&interceptor->adapter_lock);

  for (adapter_cnt = 0, entry = interceptor->adapter_list.Flink; 
       (entry != &interceptor->adapter_list) && (adapter_cnt < 32); 
       entry = entry->Flink)
    {
      adapter = CONTAINING_RECORD(entry, SshNdisIMAdapterStruct, link);

      if (!IsListEmpty(&adapter->send_wait_queue))
        {
          adapter_lookup_table[adapter_cnt] = adapter;
          adapter_cnt++;
        }
    }

  ssh_kernel_rw_mutex_unlock_read(&interceptor->adapter_lock);

  do
    {
      packets_sent = 0;

      for (i = 0; i < adapter_cnt; i++)
        {
          PLIST_ENTRY  pkt_entry;
          PNDIS_PACKET pkt;

          adapter = adapter_lookup_table[i];

          if (adapter == NULL)
            continue;

          pkt_entry = 
            NdisInterlockedRemoveHeadList(&adapter->send_wait_queue,
                                          &adapter->send_wait_queue_lock);
          if (pkt_entry == NULL)
            {
              adapter_lookup_table[i] = NULL;
              continue;
            }

#ifdef _WIN32_WCE
          if (adapter->media == NdisMediumWan)
            {
              PNDIS_WAN_PACKET wan_pkt;
              SshInterceptorProtocol protocol;

              wan_pkt = CONTAINING_RECORD(pkt_entry,
                                          NDIS_WAN_PACKET, WanPacketQueue);

              protocol = (SshInterceptorProtocol)wan_pkt->MacReserved1;
              wan_pkt->MacReserved1 = NULL;

              if (!ssh_driver_copy_and_send_wan(adapter, wan_pkt, protocol))
                {
                  /* Put this packet back to list. */
                  NdisInterlockedInsertHeadList(&adapter->send_wait_queue,
                                           &wan_pkt->WanPacketQueue,
                                           &adapter->send_wait_queue_lock);
                  adapter_lookup_table[i] = NULL;
                }
              else
                {
                  InterlockedDecrement(&interceptor->delayed_sends);
                  packets_sent++;

                  /* Indicate that this packet was sent. */
                  NdisMWanSendComplete(adapter->handle, wan_pkt,
                                       NDIS_STATUS_SUCCESS);
                }
            }
          else
#endif /* _WIN32_WCE */
            {
              pkt = CONTAINING_RECORD(pkt_entry, NDIS_PACKET,
                                      MiniportReserved);

              if (!ssh_driver_copy_and_send(adapter, pkt))
                {
                  /* Put this packet back to list. */
                  NdisInterlockedInsertHeadList(&adapter->send_wait_queue,
                                  (PLIST_ENTRY)&(pkt->MiniportReserved[0]),
                                  &adapter->send_wait_queue_lock);

                  adapter_lookup_table[i] = NULL;
                }
              else
                {
                  InterlockedDecrement(&interceptor->delayed_sends);
                  packets_sent++;
                }
            }
        }
    }
  while (packets_sent);

  SSH_LOWER_IRQL(old_irql);
}
Ejemplo n.º 6
0
VOID
TXFlushSendQueue(
    _In_  PMP_ADAPTER  Adapter,
    _In_  NDIS_STATUS  CompleteStatus)
/*++

Routine Description:

    This routine is called by the Halt or Reset handler to fail all
    the queued up Send NBLs because the device is either gone, being
    stopped for resource rebalance, or reset.

Arguments:

    Adapter                     Pointer to our adapter
    CompleteStatus              The status code with which to complete each NBL

Return Value:

    None.

--*/
{
    PTCB Tcb;

    DEBUGP(MP_TRACE, "[%p] ---> TXFlushSendQueue Status = 0x%08x\n", Adapter, CompleteStatus);


    //
    // First, free anything queued in the driver.
    //

    while (TRUE)
    {
        PLIST_ENTRY pEntry;
        PNET_BUFFER NetBuffer;
        PNET_BUFFER_LIST NetBufferList;

        pEntry = NdisInterlockedRemoveHeadList(
                &Adapter->SendWaitList,
                &Adapter->SendWaitListLock);

        if (!pEntry)
        {
            // End of list -- nothing left to free.
            break;
        }

        NetBuffer = NB_FROM_SEND_WAIT_LIST(pEntry);
        NetBufferList = NBL_FROM_SEND_NB(NetBuffer);

        DEBUGP(MP_TRACE, "[%p] Dropping Send NB: 0x%p.\n", Adapter, NetBuffer);

        NET_BUFFER_LIST_STATUS(NetBufferList) = CompleteStatus;
        TXNblRelease(Adapter, NetBufferList, FALSE);
    }


    //
    // Next, cancel anything queued in the hardware.
    //

    while (NULL != (Tcb = TXGetNextTcbToSend(Adapter)))
    {
        NET_BUFFER_LIST_STATUS(NBL_FROM_SEND_NB(Tcb->NetBuffer)) = CompleteStatus;
        ReturnTCB(Adapter, Tcb);
    }


    DEBUGP(MP_TRACE, "[%p] <--- TXFlushSendQueue\n", Adapter);
}
Ejemplo n.º 7
0
VOID
#pragma prefast(suppress: 28167, "PREfast does not recognize IRQL is conditionally raised and lowered")
TXTransmitQueuedSends(
    _In_  PMP_ADAPTER  Adapter,
    _In_  BOOLEAN      fAtDispatch)
/*++

Routine Description:

    This routine sends as many frames from the SendWaitList as it can.

    If there are not enough resources to send immediately, this function stops
    and leaves the remaining frames on the SendWaitList, to be sent once there
    are enough resources.


    Runs at IRQL <= DISPATCH_LEVEL

Arguments:

    Adapter                     Our adapter
    fAtDispatch                 TRUE if the current IRQL is DISPATCH_LEVEL

Return Value:

    None.

--*/
{
    BOOLEAN fScheduleTheSendCompleteDpc = FALSE;
    ULONG NumFramesSent = 0;
    KIRQL OldIrql = PASSIVE_LEVEL;

    DEBUGP(MP_TRACE,
           "[%p] ---> TXTransmitQueuedSends\n",
           Adapter);

    //
    // This guard ensures that only one CPU is running this function at a time.
    // We check this so that items from the SendWaitList get sent to the
    // receiving adapters in the same order that they were queued.
    //
    // You could remove this guard and everything will still work ok, but some
    // frames might be delivered out-of-order.
    //
    // Generally, this mechanism wouldn't be applicable to real hardware, since
    // the hardware would have its own mechanism to ensure sends are transmitted
    // in the correct order.
    //
    if (!fAtDispatch)
    {
        KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
    }

    if (KeTryToAcquireSpinLockAtDpcLevel(&Adapter->SendPathSpinLock))
    {
        for (NumFramesSent = 0; NumFramesSent < NIC_MAX_SENDS_PER_DPC; NumFramesSent++)
        {
            PLIST_ENTRY pTcbEntry = NULL;
            PTCB Tcb = NULL;
            PLIST_ENTRY pQueuedSend = NULL;
            PNET_BUFFER NetBuffer;

            //
            // Get the next available TCB.
            //
            pTcbEntry = NdisInterlockedRemoveHeadList(
                    &Adapter->FreeTcbList,
                    &Adapter->FreeTcbListLock);
            if (!pTcbEntry)
            {
                //
                // The adapter can't handle any more simultaneous transmit
                // operations.  Keep any remaining sends in the SendWaitList and
                // we'll come back later when there are TCBs available.
                //
                break;
            }

            Tcb = CONTAINING_RECORD(pTcbEntry, TCB, TcbLink);

            //
            // Get the next NB that needs sending.
            //
            pQueuedSend = NdisInterlockedRemoveHeadList(
                    &Adapter->SendWaitList,
                    &Adapter->SendWaitListLock);
            if (!pQueuedSend)
            {
                //
                // There's nothing left that needs sending.  We're all done.
                //
                NdisInterlockedInsertTailList(
                        &Adapter->FreeTcbList,
                        &Tcb->TcbLink,
                        &Adapter->FreeTcbListLock);
                break;
            }

            NetBuffer = NB_FROM_SEND_WAIT_LIST(pQueuedSend);


            //
            // We already packed the frame type into the net buffer before accepting
            // it for send.  Now that we have a TCB to keep track of the data, let's
            // pull it out and keep it in a proper variable.
            //
            Tcb->FrameType = FRAME_TYPE_FROM_SEND_NB(NetBuffer);

            HWProgramDmaForSend(Adapter, Tcb, NetBuffer, fAtDispatch);

            NdisInterlockedInsertTailList(
                &Adapter->BusyTcbList,
                    &Tcb->TcbLink,
                    &Adapter->BusyTcbListLock);

            fScheduleTheSendCompleteDpc = TRUE;
        }

        KeReleaseSpinLock(&Adapter->SendPathSpinLock, DISPATCH_LEVEL);
    }

    if (!fAtDispatch)
    {
        KeLowerIrql(OldIrql);
    }

    DEBUGP(MP_TRACE, "[%p] %i Frames transmitted.\n", Adapter, NumFramesSent);

    if (fScheduleTheSendCompleteDpc)
    {
        TXScheduleTheSendComplete(Adapter);
    }

    DEBUGP(MP_TRACE, "[%p] <-- TXTransmitQueuedSends\n", Adapter);
}
Ejemplo n.º 8
0
VOID
RXReceiveIndicate(
    _In_ PMP_ADAPTER Adapter,
    _In_ PMP_ADAPTER_RECEIVE_DPC AdapterDpc,
    BOOLEAN AtDpc)
/*++

Routine Description:

    This function performs the receive indications for the specified RECEIVE_DPC structure.

    Runs at IRQL <= DISPATCH_LEVEL.

Arguments:

    Adapter             Pointer to our adapter
    AdapterDpc          PMP_ADAPTER_RECEIVE_DPC structure for this receive
    AtDpc               TRUE if the function was called from the context of the DPC, FALSE if called from work item (to avoid watchdog)

Return Value:

    None.

--*/

{

    ULONG NumNblsReceived = 0;
    PNET_BUFFER_LIST FirstNbl = NULL, LastNbl = NULL;
    USHORT CurrentQueue;

    DEBUGP(MP_TRACE, "[%p] ---> RXReceiveIndicate. Processor: %i, AtDpc: %i\n", Adapter, AdapterDpc->ProcessorNumber, AtDpc);

    //
    // Exit DPC if we've queued a work item to avoid DPC watchdog timer expiration
    //
    if(AtDpc && WorkItemQueuedForWatchdogAvoidance(AdapterDpc->WorkItem,
                                                   &AdapterDpc->WorkItemQueued,
                                                   RXReceiveIndicateWorkItem,
                                                   AdapterDpc))
    {
        DEBUGP(MP_TRACE, "[%p] <--- RXReceiveIndicate. Processor: %i\n", Adapter, AdapterDpc->ProcessorNumber);
        return;
    }

    for(CurrentQueue = 0; CurrentQueue <NIC_SUPPORTED_NUM_QUEUES; ++CurrentQueue)
    {
        //
        // Consume RCBs for queue if we're the assigned consumer
        //
        if(AdapterDpc->RecvBlock[CurrentQueue])
        {
            PMP_ADAPTER_RECEIVE_BLOCK ReceiveBlock = &Adapter->ReceiveBlock[CurrentQueue];
            FirstNbl = LastNbl = NULL;

            //
            // Collect pending NBLs, indicate up to MaxNblCountPerIndicate per receive block
            //
            for(NumNblsReceived=0; NumNblsReceived < AdapterDpc->MaxNblCountPerIndicate; ++NumNblsReceived)
            {
                PLIST_ENTRY Entry;
                PRCB Rcb = NULL;

                Entry = NdisInterlockedRemoveHeadList(&ReceiveBlock->ReceiveList, &ReceiveBlock->ReceiveListLock);
                if(Entry)
                {
                    Rcb = CONTAINING_RECORD(Entry, RCB, RcbLink);
                }

                if(!Rcb)
                {
                    break;
                }

                ASSERT(Rcb->Data);

                //
                // The recv NBL's data was filled out by the hardware.  Now just update
                // its bookkeeping.
                //
                NET_BUFFER_LIST_STATUS(Rcb->Nbl) = NDIS_STATUS_SUCCESS;
                Rcb->Nbl->SourceHandle = Adapter->AdapterHandle;

                //
                // Add this NBL to the chain of NBLs to indicate up.
                //
                if (!FirstNbl)
                {
                    LastNbl = FirstNbl = Rcb->Nbl;
                }
                else
                {
                    NET_BUFFER_LIST_NEXT_NBL(LastNbl) = Rcb->Nbl;
                    LastNbl = Rcb->Nbl;
                }
            }

            //
            // Indicate NBLs
            //
            if (FirstNbl)
            {
                DEBUGP(MP_TRACE, "[%p] Receive Block %i: %i frames indicated.\n", Adapter, CurrentQueue, NumNblsReceived);

                NET_BUFFER_LIST_NEXT_NBL(LastNbl) = NULL;

                //
                // Indicate up the NBLs.
                //
                // The NDIS_RECEIVE_FLAGS_DISPATCH_LEVEL allows a perf optimization:
                // NDIS doesn't have to check and raise the current IRQL, since we
                // promise that the current IRQL is exactly DISPATCH_LEVEL already.
                //
                NdisMIndicateReceiveNetBufferLists(
                        Adapter->AdapterHandle,
                        FirstNbl,
                        0,  // default port
                        NumNblsReceived,
                        (AtDpc?NDIS_RECEIVE_FLAGS_DISPATCH_LEVEL:0)
                        | NDIS_RECEIVE_FLAGS_PERFECT_FILTERED
#if (NDIS_SUPPORT_NDIS620)
                        | NDIS_RECEIVE_FLAGS_SINGLE_QUEUE
                        | (CurrentQueue?NDIS_RECEIVE_FLAGS_SHARED_MEMORY_INFO_VALID:0) //non-default queues use shared memory
#endif
                        );
            }

            if(!AtDpc)
            {
                //
                // Clear work item flag to allow DPCs to be queued
                //
                InterlockedExchange(&AdapterDpc->WorkItemQueued, FALSE);
            }

            if (!IsListEmpty(&ReceiveBlock->ReceiveList))
            {
                //
                // More left to indicate for this receive block, queue this DPC again
                //
                DEBUGP(MP_TRACE, "[%p] Receive Block %i: Requeued DPC.\n", Adapter, CurrentQueue);
                KeInsertQueueDpc(&AdapterDpc->Dpc, AdapterDpc, NULL);
            }

        }

    }

    DEBUGP(MP_TRACE, "[%p] <--- RXReceiveIndicate. Processor: %i\n", Adapter, AdapterDpc->ProcessorNumber);
}
Ejemplo n.º 9
0
PRCB
GetRCB(
    __in  PMP_ADAPTER  Adapter,
    __in  PNDIS_NET_BUFFER_LIST_8021Q_INFO Nbl1QInfo,
    __in  PFRAME       Frame)
/*++

Routine Description:

    This routine gets an unused RCB from the pool.

    Runs at IRQL <= DISPATCH_LEVEL

Arguments:

    Adapter                     The receiving adapter
    Nbl1QInfo                   8021Q Tag information for the FRAME being received
    Frame                       The frame that will be attached to the RCB

Return Value:

    NULL if an RCB could not be allocated.
    Else, a pointer to an initialized RCB.

--*/
{
    NDIS_STATUS Status = NDIS_STATUS_RESOURCES;
    PRCB Rcb = NULL;

    DEBUGP(MP_TRACE, "[%p] ---> GetRCB.\n", Adapter);

    if(VMQ_ENABLED(Adapter))
    {
        //
        // Retrieve the RCB from the target VMQ queue for the frame
        //
        Status = GetRcbForRxQueue(Adapter, Frame, Nbl1QInfo, &Rcb);
    }
    else
    {
        //
        // Retrieve the RCB from the global RCB pool
        //
        PLIST_ENTRY pEntry = NdisInterlockedRemoveHeadList(
                &Adapter->FreeRcbList,
                &Adapter->FreeRcbListLock);
        if (pEntry)
        {
            Rcb = CONTAINING_RECORD(pEntry, RCB, RcbLink);
            //
            // Receiving on the default receive queue, increment its pending count
            //
            Status = NICReferenceReceiveBlock(Adapter, 0);
            if(Status != NDIS_STATUS_SUCCESS)
            {
                //
                // The adapter is no longer in a ready state, so we were not able to take a reference on the
                // receive block. Add the RCB back to the free list and fail this receive. 
                //
                NdisInterlockedInsertTailList(
                    &Adapter->FreeRcbList,
                    &Rcb->RcbLink,
                    &Adapter->FreeRcbListLock);
                Rcb = NULL;
            }
        }
    }

    if (Rcb)
    {
        //
        // Simulate the hardware DMA'ing the received frame into the NB's MDL.
        //
        Status = HWBeginReceiveDma(Adapter, Nbl1QInfo, Rcb, Frame);
        if(Status != NDIS_STATUS_SUCCESS)
        {
            DEBUGP(MP_TRACE, "[%p] HWBeginReceiveDma failed with error 0x%08x, aborting RCB allocation.\n", Adapter, Status);
            //
            // Increase failure counters if appropriate
            //
            if(Status == NDIS_STATUS_RESOURCES)
            {
                ++Adapter->RxResourceErrors;   
            }
            else if(Status != NDIS_STATUS_INVALID_ADDRESS)
            {
                ++Adapter->RxRuntErrors;
            }
            //
            // Recover RCB 
            //
            ReturnRCB(Adapter, Rcb);
            Rcb = NULL;
        }
    }
    else
    {
        DEBUGP(MP_LOUD, "[%p] An RCB could not be retrieved. Status: 0x%08x.\n", Adapter, Status);
        ++Adapter->RxResourceErrors;
    }

    DEBUGP(MP_LOUD, "[%p] Allocated RCB: %p.", Adapter, Rcb);
    DEBUGP(MP_TRACE, "[%p] <--- GetRCB.\n", Adapter);

    return Rcb;
}