vbox: rework network backend

* Handle signals and thereby work asynchronously
* Forward link-state changes to the attached AboveNetwork

Fixes #1465.
This commit is contained in:
Josef Söntgen 2015-03-20 15:48:19 +01:00 committed by Christian Helmuth
parent 19f8666170
commit e53e6b0c5c
1 changed files with 388 additions and 299 deletions

View File

@ -4,7 +4,7 @@
*/ */
/* /*
* Copyright (C) 2014 Genode Labs GmbH * Copyright (C) 2014-2015 Genode Labs GmbH
* *
* This file is distributed under the terms of the GNU General Public License * This file is distributed under the terms of the GNU General Public License
* version 2. * version 2.
@ -42,243 +42,338 @@
/******************************************************************************* /*******************************************************************************
* Structures and Typedefs * * Structures and Typedefs *
*******************************************************************************/ *******************************************************************************/
struct Nic_client;
/** /**
* TAP driver instance data. * Nic driver instance data.
* *
* @implements PDMINETWORKUP * @implements PDMINETWORKUP
*/ */
typedef struct DRVTAP typedef struct DRVNIC
{ {
/** The network interface. */ /** The network interface to Nic session. */
PDMINETWORKUP INetworkUp; PDMINETWORKUP INetworkUp;
/** The network interface. */ /** The config port interface we're representing. */
PPDMINETWORKDOWN pIAboveNet; PDMINETWORKCONFIG INetworkConfig;
/** Pointer to the driver instance. */ /** The network interface to VBox driver. */
PPDMDRVINS pDrvIns; PPDMINETWORKDOWN pIAboveNet;
/** Reader thread. */ /** The config port interface we're attached to. */
PPDMTHREAD pThread; PPDMINETWORKCONFIG pIAboveConfig;
/** Pointer to the driver instance. */
/** @todo The transmit thread. */ PPDMDRVINS pDrvIns;
/** Transmit lock used by drvTAPNetworkUp_BeginXmit. */ /** Receiver thread that handles all signals. */
RTCRITSECT XmitLock; PPDMTHREAD pThread;
/** Nic::Session client wrapper. */
Nic::Session *nic_session; Nic_client *nic_client;
} DRVNIC, *PDRVNIC;
PDMINETWORKCONFIG INetworkConfig;
} DRVTAP, *PDRVTAP;
/** Converts a pointer to TAP::INetworkUp to a PRDVTAP. */ class Nic_client
#define PDMINETWORKUP_2_DRVTAP(pInterface) ( (PDRVTAP)((uintptr_t)pInterface - RT_OFFSETOF(DRVTAP, INetworkUp)) ) {
#define PDMINETWORKCONFIG_2_DRVTAP(pInterface) ( (PDRVTAP)((uintptr_t)pInterface - RT_OFFSETOF(DRVTAP, INetworkConfig)) ) private:
enum {
PACKET_SIZE = Nic::Packet_allocator::DEFAULT_PACKET_SIZE,
BUF_SIZE = Nic::Session::QUEUE_SIZE * PACKET_SIZE,
};
Nic::Packet_allocator *_tx_block_alloc;
Nic::Connection _nic;
Genode::Signal_receiver _sig_rec;
Genode::Signal_dispatcher<Nic_client> _link_state_dispatcher;
Genode::Signal_dispatcher<Nic_client> _rx_packet_avail_dispatcher;
Genode::Signal_dispatcher<Nic_client> _rx_ready_to_ack_dispatcher;
Genode::Signal_dispatcher<Nic_client> _destruct_dispatcher;
/* VM <-> device driver (down) <-> nic_client (up) <-> nic session */
PPDMINETWORKDOWN _down_rx;
PPDMINETWORKCONFIG _down_rx_config;
void _handle_rx_packet_avail(unsigned)
{
while (_nic.rx()->packet_avail() && _nic.rx()->ready_to_ack()) {
Nic::Packet_descriptor rx_packet = _nic.rx()->get_packet();
char *rx_content = _nic.rx()->packet_content(rx_packet);
int rc = _down_rx->pfnWaitReceiveAvail(_down_rx, RT_INDEFINITE_WAIT);
if (RT_FAILURE(rc))
continue;
rc = _down_rx->pfnReceive(_down_rx, rx_content, rx_packet.size());
AssertRC(rc);
_nic.rx()->acknowledge_packet(rx_packet);
}
}
void _handle_rx_ready_to_ack(unsigned) { _handle_rx_packet_avail(0); }
void _handle_link_state(unsigned)
{
_down_rx_config->pfnSetLinkState(_down_rx_config,
_nic.link_state() ? PDMNETWORKLINKSTATE_UP
: PDMNETWORKLINKSTATE_DOWN);
}
/**
* By handling this signal the I/O thread gets unblocked
* and will leave its loop when the DRVNIC instance is
* being destructed.
*/
void _handle_destruct(unsigned) { }
void _tx_ack(bool block = false)
{
/* check for acknowledgements */
while (_nic.tx()->ack_avail() || block) {
Nic::Packet_descriptor acked_packet = _nic.tx()->get_acked_packet();
_nic.tx()->release_packet(acked_packet);
block = false;
}
}
Nic::Packet_descriptor _alloc_tx_packet(Genode::size_t len)
{
while (true) {
try {
Nic::Packet_descriptor packet = _nic.tx()->alloc_packet(len);
return packet;
} catch (Nic::Session::Tx::Source::Packet_alloc_failed) {
_tx_ack(true);
}
}
}
static Nic::Packet_allocator* _packet_allocator()
{
using namespace Genode;
return new (env()->heap())Nic::Packet_allocator(env()->heap());
}
public:
Nic_client(PDRVNIC drvtap)
:
_tx_block_alloc(_packet_allocator()),
_nic(_tx_block_alloc, BUF_SIZE, BUF_SIZE),
_link_state_dispatcher(_sig_rec, *this, &Nic_client::_handle_link_state),
_rx_packet_avail_dispatcher(_sig_rec, *this, &Nic_client::_handle_rx_packet_avail),
_rx_ready_to_ack_dispatcher(_sig_rec, *this, &Nic_client::_handle_rx_ready_to_ack),
_destruct_dispatcher(_sig_rec, *this, &Nic_client::_handle_destruct),
_down_rx(drvtap->pIAboveNet),
_down_rx_config(drvtap->pIAboveConfig)
{
_nic.link_state_sigh(_link_state_dispatcher);
_nic.rx_channel()->sigh_packet_avail(_rx_packet_avail_dispatcher);
_nic.rx_channel()->sigh_ready_to_ack(_rx_ready_to_ack_dispatcher);
}
~Nic_client()
{
using namespace Genode;
destroy(env()->heap(), _tx_block_alloc);
}
Genode::Signal_context_capability &dispatcher() { return _destruct_dispatcher; }
Genode::Signal_receiver &sig_rec() { return _sig_rec; }
Nic::Mac_address mac_address() { return _nic.mac_address(); }
int send_packet(void *packet, uint32_t packet_len)
{
Nic::Packet_descriptor tx_packet = _alloc_tx_packet(packet_len);
char *tx_content = _nic.tx()->packet_content(tx_packet);
Genode::memcpy(tx_content, packet, packet_len);
_nic.tx()->submit_packet(tx_packet);
_tx_ack();
return VINF_SUCCESS;
}
};
/**
* Return lock to synchronize the destruction of the
* PDRVNIC, i.e., the Nic_client.
*/
static Genode::Lock *destruct_lock()
{
static Genode::Lock lock(Genode::Lock::LOCKED);
return &lock;
}
/** Converts a pointer to Nic::INetworkUp to a PRDVNic. */
#define PDMINETWORKUP_2_DRVNIC(pInterface) ( (PDRVNIC)((uintptr_t)pInterface - RT_OFFSETOF(DRVNIC, INetworkUp)) )
#define PDMINETWORKCONFIG_2_DRVNIC(pInterface) ( (PDRVNIC)((uintptr_t)pInterface - RT_OFFSETOF(DRVNIC, INetworkConfig)) )
/******************************************************************************* /*******************************************************************************
* Internal Functions * * Internal Functions *
*******************************************************************************/ *******************************************************************************/
static int net_send_packet(void * packet, uint32_t packet_len, Nic::Session * nic) {
/* allocate transmit packet */
Nic::Packet_descriptor tx_packet;
try {
tx_packet = nic->tx()->alloc_packet(packet_len);
} catch (Nic::Session::Tx::Source::Packet_alloc_failed) {
PERR("tx packet alloc failed");
return VERR_NO_MEMORY;
}
/* fill packet with content */
char * stream_buffer = nic->tx()->packet_content(tx_packet);
memcpy(stream_buffer, packet, packet_len);
nic->tx()->submit_packet(tx_packet);
/* wait for acknowledgement */
Nic::Packet_descriptor ack_tx_packet = nic->tx()->get_acked_packet();
if (ack_tx_packet.size() != tx_packet.size() ||
ack_tx_packet.offset() != tx_packet.offset())
PERR("unexpected acked packet");
nic->tx()->release_packet(tx_packet);
return VINF_SUCCESS;
}
/** /**
* @interface_method_impl{PDMINETWORKUP,pfnBeginXmit} * @interface_method_impl{PDMINETWORKUP,pfnBeginXmit}
*/ */
static DECLCALLBACK(int) drvTAPNetworkUp_BeginXmit(PPDMINETWORKUP pInterface, bool fOnWorkerThread) static DECLCALLBACK(int) drvNicNetworkUp_BeginXmit(PPDMINETWORKUP pInterface, bool fOnWorkerThread)
{ {
PDRVTAP pThis = PDMINETWORKUP_2_DRVTAP(pInterface); return VINF_SUCCESS;
int rc = RTCritSectTryEnter(&pThis->XmitLock);
if (RT_FAILURE(rc))
{
/** @todo XMIT thread */
rc = VERR_TRY_AGAIN;
}
return rc;
} }
/** /**
* @interface_method_impl{PDMINETWORKUP,pfnAllocBuf} * @interface_method_impl{PDMINETWORKUP,pfnAllocBuf}
*/ */
static DECLCALLBACK(int) drvTAPNetworkUp_AllocBuf(PPDMINETWORKUP pInterface, size_t cbMin, static DECLCALLBACK(int) drvNicNetworkUp_AllocBuf(PPDMINETWORKUP pInterface, size_t cbMin,
PCPDMNETWORKGSO pGso, PPPDMSCATTERGATHER ppSgBuf) PCPDMNETWORKGSO pGso, PPPDMSCATTERGATHER ppSgBuf)
{ {
PDRVTAP pThis = PDMINETWORKUP_2_DRVTAP(pInterface); PDRVNIC pThis = PDMINETWORKUP_2_DRVNIC(pInterface);
Assert(RTCritSectIsOwner(&pThis->XmitLock));
/* /*
* Allocate a scatter / gather buffer descriptor that is immediately * Allocate a scatter / gather buffer descriptor that is immediately
* followed by the buffer space of its single segment. The GSO context * followed by the buffer space of its single segment. The GSO context
* comes after that again. * comes after that again.
*/ */
PPDMSCATTERGATHER pSgBuf = (PPDMSCATTERGATHER)RTMemAlloc( RT_ALIGN_Z(sizeof(*pSgBuf), 16) PPDMSCATTERGATHER pSgBuf = (PPDMSCATTERGATHER)RTMemAlloc(RT_ALIGN_Z(sizeof(*pSgBuf), 16)
+ RT_ALIGN_Z(cbMin, 16) + RT_ALIGN_Z(cbMin, 16)
+ (pGso ? RT_ALIGN_Z(sizeof(*pGso), 16) : 0)); + (pGso ? RT_ALIGN_Z(sizeof(*pGso), 16) : 0));
if (!pSgBuf) if (!pSgBuf)
return VERR_NO_MEMORY; return VERR_NO_MEMORY;
/* /*
* Initialize the S/G buffer and return. * Initialize the S/G buffer and return.
*/ */
pSgBuf->fFlags = PDMSCATTERGATHER_FLAGS_MAGIC | PDMSCATTERGATHER_FLAGS_OWNER_1; pSgBuf->fFlags = PDMSCATTERGATHER_FLAGS_MAGIC | PDMSCATTERGATHER_FLAGS_OWNER_1;
pSgBuf->cbUsed = 0; pSgBuf->cbUsed = 0;
pSgBuf->cbAvailable = RT_ALIGN_Z(cbMin, 16); pSgBuf->cbAvailable = RT_ALIGN_Z(cbMin, 16);
pSgBuf->pvAllocator = NULL; pSgBuf->pvAllocator = NULL;
if (!pGso) if (!pGso)
pSgBuf->pvUser = NULL; pSgBuf->pvUser = NULL;
else else
{ {
pSgBuf->pvUser = (uint8_t *)(pSgBuf + 1) + pSgBuf->cbAvailable; pSgBuf->pvUser = (uint8_t *)(pSgBuf + 1) + pSgBuf->cbAvailable;
*(PPDMNETWORKGSO)pSgBuf->pvUser = *pGso; *(PPDMNETWORKGSO)pSgBuf->pvUser = *pGso;
} }
pSgBuf->cSegs = 1; pSgBuf->cSegs = 1;
pSgBuf->aSegs[0].cbSeg = pSgBuf->cbAvailable; pSgBuf->aSegs[0].cbSeg = pSgBuf->cbAvailable;
pSgBuf->aSegs[0].pvSeg = pSgBuf + 1; pSgBuf->aSegs[0].pvSeg = pSgBuf + 1;
#if 0 /* poison */ *ppSgBuf = pSgBuf;
memset(pSgBuf->aSegs[0].pvSeg, 'F', pSgBuf->aSegs[0].cbSeg); return VINF_SUCCESS;
#endif
*ppSgBuf = pSgBuf;
return VINF_SUCCESS;
} }
/** /**
* @interface_method_impl{PDMINETWORKUP,pfnFreeBuf} * @interface_method_impl{PDMINETWORKUP,pfnFreeBuf}
*/ */
static DECLCALLBACK(int) drvTAPNetworkUp_FreeBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf) static DECLCALLBACK(int) drvNicNetworkUp_FreeBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf)
{ {
PDRVTAP pThis = PDMINETWORKUP_2_DRVTAP(pInterface); PDRVNIC pThis = PDMINETWORKUP_2_DRVNIC(pInterface);
Assert(RTCritSectIsOwner(&pThis->XmitLock)); if (pSgBuf)
if (pSgBuf) {
{ Assert((pSgBuf->fFlags & PDMSCATTERGATHER_FLAGS_MAGIC_MASK) == PDMSCATTERGATHER_FLAGS_MAGIC);
Assert((pSgBuf->fFlags & PDMSCATTERGATHER_FLAGS_MAGIC_MASK) == PDMSCATTERGATHER_FLAGS_MAGIC); pSgBuf->fFlags = 0;
pSgBuf->fFlags = 0; RTMemFree(pSgBuf);
RTMemFree(pSgBuf); }
} return VINF_SUCCESS;
return VINF_SUCCESS;
} }
/** /**
* @interface_method_impl{PDMINETWORKUP,pfnSendBuf} * @interface_method_impl{PDMINETWORKUP,pfnSendBuf}
*/ */
static DECLCALLBACK(int) drvTAPNetworkUp_SendBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf, bool fOnWorkerThread) static DECLCALLBACK(int) drvNicNetworkUp_SendBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf, bool fOnWorkerThread)
{ {
PDRVTAP pThis = PDMINETWORKUP_2_DRVTAP(pInterface); PDRVNIC pThis = PDMINETWORKUP_2_DRVNIC(pInterface);
Nic_client *nic_client = pThis->nic_client;
AssertPtr(pSgBuf); AssertPtr(pSgBuf);
Assert((pSgBuf->fFlags & PDMSCATTERGATHER_FLAGS_MAGIC_MASK) == PDMSCATTERGATHER_FLAGS_MAGIC); Assert((pSgBuf->fFlags & PDMSCATTERGATHER_FLAGS_MAGIC_MASK) == PDMSCATTERGATHER_FLAGS_MAGIC);
Assert(RTCritSectIsOwner(&pThis->XmitLock));
/* Set an FTM checkpoint as this operation changes the state permanently. */ /* Set an FTM checkpoint as this operation changes the state permanently. */
PDMDrvHlpFTSetCheckpoint(pThis->pDrvIns, FTMCHECKPOINTTYPE_NETWORK); PDMDrvHlpFTSetCheckpoint(pThis->pDrvIns, FTMCHECKPOINTTYPE_NETWORK);
int rc; int rc;
if (!pSgBuf->pvUser) if (!pSgBuf->pvUser)
{ {
Log2(("drvTAPSend: pSgBuf->aSegs[0].pvSeg=%p pSgBuf->cbUsed=%#x\n" Log2(("drvNicSend: pSgBuf->aSegs[0].pvSeg=%p pSgBuf->cbUsed=%#x\n"
"%.*Rhxd\n", "%.*Rhxd\n", pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed, pSgBuf->cbUsed,
pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed, pSgBuf->cbUsed, pSgBuf->aSegs[0].pvSeg)); pSgBuf->aSegs[0].pvSeg));
rc = net_send_packet(pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed, pThis->nic_session); rc = nic_client->send_packet(pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed);
} }
else else
{ {
uint8_t abHdrScratch[256]; uint8_t abHdrScratch[256];
uint8_t const *pbFrame = (uint8_t const *)pSgBuf->aSegs[0].pvSeg; uint8_t const *pbFrame = (uint8_t const *)pSgBuf->aSegs[0].pvSeg;
PCPDMNETWORKGSO pGso = (PCPDMNETWORKGSO)pSgBuf->pvUser; PCPDMNETWORKGSO pGso = (PCPDMNETWORKGSO)pSgBuf->pvUser;
uint32_t const cSegs = PDMNetGsoCalcSegmentCount(pGso, pSgBuf->cbUsed); Assert(cSegs > 1); uint32_t const cSegs = PDMNetGsoCalcSegmentCount(pGso, pSgBuf->cbUsed);
rc = VINF_SUCCESS; Assert(cSegs > 1);
for (size_t iSeg = 0; iSeg < cSegs; iSeg++) rc = VINF_SUCCESS;
{ for (size_t iSeg = 0; iSeg < cSegs; iSeg++)
uint32_t cbSegFrame; {
void *pvSegFrame = PDMNetGsoCarveSegmentQD(pGso, (uint8_t *)pbFrame, pSgBuf->cbUsed, abHdrScratch, uint32_t cbSegFrame;
iSeg, cSegs, &cbSegFrame); void *pvSegFrame = PDMNetGsoCarveSegmentQD(pGso, (uint8_t *)pbFrame, pSgBuf->cbUsed,
rc = net_send_packet(pvSegFrame, cbSegFrame, pThis->nic_session); abHdrScratch, iSeg, cSegs, &cbSegFrame);
if (RT_FAILURE(rc)) rc = nic_client->send_packet(pvSegFrame, cbSegFrame);
break; if (RT_FAILURE(rc))
} break;
} }
}
pSgBuf->fFlags = 0; pSgBuf->fFlags = 0;
RTMemFree(pSgBuf); RTMemFree(pSgBuf);
AssertRC(rc); AssertRC(rc);
if (RT_FAILURE(rc)) if (RT_FAILURE(rc))
rc = rc == VERR_NO_MEMORY ? VERR_NET_NO_BUFFER_SPACE : VERR_NET_DOWN; rc = rc == VERR_NO_MEMORY ? VERR_NET_NO_BUFFER_SPACE : VERR_NET_DOWN;
return rc; return rc;
} }
/** /**
* @interface_method_impl{PDMINETWORKUP,pfnEndXmit} * @interface_method_impl{PDMINETWORKUP,pfnEndXmit}
*/ */
static DECLCALLBACK(void) drvTAPNetworkUp_EndXmit(PPDMINETWORKUP pInterface) static DECLCALLBACK(void) drvNicNetworkUp_EndXmit(PPDMINETWORKUP pInterface)
{ {
PDRVTAP pThis = PDMINETWORKUP_2_DRVTAP(pInterface); PDRVNIC pThis = PDMINETWORKUP_2_DRVNIC(pInterface);
RTCritSectLeave(&pThis->XmitLock);
} }
/** /**
* @interface_method_impl{PDMINETWORKUP,pfnSetPromiscuousMode} * @interface_method_impl{PDMINETWORKUP,pfnSetPromiscuousMode}
*/ */
static DECLCALLBACK(void) drvTAPNetworkUp_SetPromiscuousMode(PPDMINETWORKUP pInterface, bool fPromiscuous) static DECLCALLBACK(void) drvNicNetworkUp_SetPromiscuousMode(PPDMINETWORKUP pInterface, bool fPromiscuous)
{ {
LogFlow(("drvTAPNetworkUp_SetPromiscuousMode: fPromiscuous=%d\n", fPromiscuous)); LogFlow(("drvNicNetworkUp_SetPromiscuousMode: fPromiscuous=%d\n", fPromiscuous));
/* nothing to do */ /* nothing to do */
} }
/** /**
* Notification on link status changes. * Notification on link status changes.
*
* @param pInterface Pointer to the interface structure containing the called function pointer.
* @param enmLinkState The new link state.
* @thread EMT
*/ */
static DECLCALLBACK(void) drvTAPNetworkUp_NotifyLinkChanged(PPDMINETWORKUP pInterface, PDMNETWORKLINKSTATE enmLinkState) static DECLCALLBACK(void) drvNicNetworkUp_NotifyLinkChanged(PPDMINETWORKUP pInterface, PDMNETWORKLINKSTATE enmLinkState)
{ {
LogFlow(("drvTAPNetworkUp_NotifyLinkChanged: enmLinkState=%d\n", enmLinkState)); LogFlow(("drvNicNetworkUp_NotifyLinkChanged: enmLinkState=%d\n", enmLinkState));
/** @todo take action on link down and up. Stop the polling and such like. */ /*
* At this point we could stop waiting for signals etc. but for now we just do nothing.
*/
} }
static DECLCALLBACK(int) drvGetMac(PPDMINETWORKCONFIG pInterface, PRTMAC pMac) static DECLCALLBACK(int) drvGetMac(PPDMINETWORKCONFIG pInterface, PRTMAC pMac)
{ {
PDRVTAP pThis = PDMINETWORKCONFIG_2_DRVTAP(pInterface); PDRVNIC pThis = PDMINETWORKCONFIG_2_DRVNIC(pInterface);
Nic_client *nic_client = pThis->nic_client;
static_assert (sizeof(*pMac) == sizeof(pThis->nic_session->mac_address()), static_assert (sizeof(*pMac) == sizeof(nic_client->mac_address()),
"should be equal"); "should be equal");
memcpy(pMac, pThis->nic_session->mac_address().addr, sizeof(*pMac)); memcpy(pMac, nic_client->mac_address().addr, sizeof(*pMac));
return VINF_SUCCESS; return VINF_SUCCESS;
} }
@ -288,53 +383,47 @@ static DECLCALLBACK(int) drvGetMac(PPDMINETWORKCONFIG pInterface, PRTMAC pMac)
* *
* @returns VINF_SUCCESS (ignored). * @returns VINF_SUCCESS (ignored).
* @param Thread Thread handle. * @param Thread Thread handle.
* @param pvUser Pointer to a DRVTAP structure. * @param pvUser Pointer to a DRVNIC structure.
*/ */
static DECLCALLBACK(int) drvTAPAsyncIoThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread) static DECLCALLBACK(int) drvNicAsyncIoThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
{ {
PDRVTAP pThis = PDMINS_2_DATA(pDrvIns, PDRVTAP); PDRVNIC pThis = PDMINS_2_DATA(pDrvIns, PDRVNIC);
LogFlow(("drvTAPAsyncIoThread: pThis=%p\n", pThis)); LogFlow(("drvNicAsyncIoThread: pThis=%p\n", pThis));
if (pThread->enmState == PDMTHREADSTATE_INITIALIZING) if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
return VINF_SUCCESS; return VINF_SUCCESS;
Nic::Session * nic = pThis->nic_session; Genode::Signal_receiver &sig_rec = pThis->nic_client->sig_rec();
while (pThread->enmState == PDMTHREADSTATE_RUNNING) while (pThread->enmState == PDMTHREADSTATE_RUNNING)
{ {
Nic::Packet_descriptor rx_packet = nic->rx()->get_packet(); Genode::Signal sig = sig_rec.wait_for_signal();
int num = sig.num();
/* send it to the network bus */ Genode::Signal_dispatcher_base *dispatcher;
char * rx_content = nic->rx()->packet_content(rx_packet); dispatcher = dynamic_cast<Genode::Signal_dispatcher_base *>(sig.context());
dispatcher->dispatch(num);
int rc1 = pThis->pIAboveNet->pfnWaitReceiveAvail(pThis->pIAboveNet,
RT_INDEFINITE_WAIT);
if (RT_FAILURE(rc1))
continue;
rc1 = pThis->pIAboveNet->pfnReceive(pThis->pIAboveNet, rx_content,
rx_packet.size());
AssertRC(rc1);
/* acknowledge received packet */
nic->rx()->acknowledge_packet(rx_packet);
} }
return VINF_SUCCESS; destruct_lock()->unlock();
return VINF_SUCCESS;
} }
/** /**
* Unblock the send thread so it can respond to a state change. * Unblock the asynchronous I/O thread.
* *
* @returns VBox status code. * @returns VBox status code.
* @param pDevIns The pcnet device instance. * @param pDevIns The pcnet device instance.
* @param pThread The send thread. * @param pThread The asynchronous I/O thread.
*/ */
static DECLCALLBACK(int) drvTapAsyncIoWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread) static DECLCALLBACK(int) drvNicAsyncIoWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
{ {
PDRVTAP pThis = PDMINS_2_DATA(pDrvIns, PDRVTAP); /*
Assert(!"Not implemented"); * Since we already wake up on singals in the I/O thread function
* we just return success at this point.
*/
return VINF_SUCCESS; return VINF_SUCCESS;
} }
@ -344,149 +433,149 @@ static DECLCALLBACK(int) drvTapAsyncIoWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThr
/** /**
* @interface_method_impl{PDMIBASE,pfnQueryInterface} * @interface_method_impl{PDMIBASE,pfnQueryInterface}
*/ */
static DECLCALLBACK(void *) drvTAPQueryInterface(PPDMIBASE pInterface, const char *pszIID) static DECLCALLBACK(void *) drvNicQueryInterface(PPDMIBASE pInterface, const char *pszIID)
{ {
PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface); PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
PDRVTAP pThis = PDMINS_2_DATA(pDrvIns, PDRVTAP); PDRVNIC pThis = PDMINS_2_DATA(pDrvIns, PDRVNIC);
PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase); PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKUP, &pThis->INetworkUp); PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKUP, &pThis->INetworkUp);
PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKCONFIG, &pThis->INetworkConfig); PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKCONFIG, &pThis->INetworkConfig);
return NULL; return NULL;
} }
/* -=-=-=-=- PDMDRVREG -=-=-=-=- */ /* -=-=-=-=- PDMDRVREG -=-=-=-=- */
static DECLCALLBACK(void) drvTAPDestruct(PPDMDRVINS pDrvIns) static DECLCALLBACK(void) drvNicDestruct(PPDMDRVINS pDrvIns)
{ {
Assert(!"Not implemented"); PDRVNIC pThis = PDMINS_2_DATA(pDrvIns, PDRVNIC);
Nic_client *nic_client = pThis->nic_client;
Genode::Signal_transmitter(nic_client->dispatcher()).submit();
/* wait until the recv thread exits */
destruct_lock()->lock();
destroy(Genode::env()->heap(), nic_client);
} }
/** /**
* Construct a TAP network transport driver instance. * Construct a Nic network transport driver instance.
* *
* @copydoc FNPDMDRVCONSTRUCT * @copydoc FNPDMDRVCONSTRUCT
*/ */
static DECLCALLBACK(int) drvTAPConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags) static DECLCALLBACK(int) drvNicConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
{ {
PDRVTAP pThis = PDMINS_2_DATA(pDrvIns, PDRVTAP); PDRVNIC pThis = PDMINS_2_DATA(pDrvIns, PDRVNIC);
PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns); PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
/*
* Init the static parts.
*/
pThis->pDrvIns = pDrvIns;
/* IBase */
pDrvIns->IBase.pfnQueryInterface = drvTAPQueryInterface;
/* INetwork */
pThis->INetworkUp.pfnBeginXmit = drvTAPNetworkUp_BeginXmit;
pThis->INetworkUp.pfnAllocBuf = drvTAPNetworkUp_AllocBuf;
pThis->INetworkUp.pfnFreeBuf = drvTAPNetworkUp_FreeBuf;
pThis->INetworkUp.pfnSendBuf = drvTAPNetworkUp_SendBuf;
pThis->INetworkUp.pfnEndXmit = drvTAPNetworkUp_EndXmit;
pThis->INetworkUp.pfnSetPromiscuousMode = drvTAPNetworkUp_SetPromiscuousMode;
pThis->INetworkUp.pfnNotifyLinkChanged = drvTAPNetworkUp_NotifyLinkChanged;
/*
* Init the static parts.
*/
pThis->pDrvIns = pDrvIns;
/* IBase */
pDrvIns->IBase.pfnQueryInterface = drvNicQueryInterface;
/* INetwork */
pThis->INetworkUp.pfnBeginXmit = drvNicNetworkUp_BeginXmit;
pThis->INetworkUp.pfnAllocBuf = drvNicNetworkUp_AllocBuf;
pThis->INetworkUp.pfnFreeBuf = drvNicNetworkUp_FreeBuf;
pThis->INetworkUp.pfnSendBuf = drvNicNetworkUp_SendBuf;
pThis->INetworkUp.pfnEndXmit = drvNicNetworkUp_EndXmit;
pThis->INetworkUp.pfnSetPromiscuousMode = drvNicNetworkUp_SetPromiscuousMode;
pThis->INetworkUp.pfnNotifyLinkChanged = drvNicNetworkUp_NotifyLinkChanged;
/* INetworkConfig - used on Genode to request Mac address of nic_session */ /* INetworkConfig - used on Genode to request Mac address of nic_session */
pThis->INetworkConfig.pfnGetMac = drvGetMac; pThis->INetworkConfig.pfnGetMac = drvGetMac;
/*
* Check that no-one is attached to us.
*/
AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
("Configuration error: Not possible to attach anything to"
" this driver!\n"), VERR_PDM_DRVINS_NO_ATTACH);
/*
* Query the above network port interface.
*/
pThis->pIAboveNet = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKDOWN);
if (!pThis->pIAboveNet)
return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
N_("Configuration error: The above device/driver"
" didn't export the network port interface"));
pThis->pIAboveConfig = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKCONFIG);
if (!pThis->pIAboveConfig)
return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
N_("Configuration error: the above device/driver"
" didn't export the network config interface!\n"));
/* /*
* Setup Genode nic_session connection * Setup Genode nic_session connection
*/ */
Nic::Packet_allocator *tx_block_alloc =
new (Genode::env()->heap()) Nic::Packet_allocator(Genode::env()->heap());
enum {
PACKET_SIZE = Nic::Packet_allocator::DEFAULT_PACKET_SIZE,
BUF_SIZE = Nic::Session::QUEUE_SIZE * PACKET_SIZE,
};
try { try {
pThis->nic_session = new Nic::Connection(tx_block_alloc, BUF_SIZE, pThis->nic_client = new (Genode::env()->heap()) Nic_client(pThis);
BUF_SIZE);
} catch (...) { } catch (...) {
return VERR_HOSTIF_INIT_FAILED; return VERR_HOSTIF_INIT_FAILED;
} }
/* /*
* Check that no-one is attached to us. * Create the asynchronous I/O thread.
*/ */
AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER, int rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pThread, pThis,
("Configuration error: Not possible to attach anything to this driver!\n"), drvNicAsyncIoThread, drvNicAsyncIoWakeup,
VERR_PDM_DRVINS_NO_ATTACH); 128 * _1K, RTTHREADTYPE_IO, "nic_thread");
AssertRCReturn(rc, rc);
/*
* Query the network port interface.
*/
pThis->pIAboveNet = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKDOWN);
if (!pThis->pIAboveNet)
return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
N_("Configuration error: The above device/driver didn't export the network port interface"));
/*
* Create the transmit lock.
*/
int rc = RTCritSectInit(&pThis->XmitLock);
AssertRCReturn(rc, rc);
/*
* Create the async I/O thread.
*/
rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pThread, pThis, drvTAPAsyncIoThread, drvTapAsyncIoWakeup, 128 * _1K, RTTHREADTYPE_IO, "TAP");
AssertRCReturn(rc, rc);
return rc; return rc;
} }
/** /**
* TAP network transport driver registration record. * Nic network transport driver registration record.
*/ */
const PDMDRVREG g_DrvHostInterface = const PDMDRVREG g_DrvHostInterface =
{ {
/* u32Version */ /* u32Version */
PDM_DRVREG_VERSION, PDM_DRVREG_VERSION,
/* szName */ /* szName */
"HostInterface", "HostInterface",
/* szRCMod */ /* szRCMod */
"", "",
/* szR0Mod */ /* szR0Mod */
"", "",
/* pszDescription */ /* pszDescription */
"Genode Network Session Driver", "Genode Network Session Driver",
/* fFlags */ /* fFlags */
PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT, PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
/* fClass. */ /* fClass. */
PDM_DRVREG_CLASS_NETWORK, PDM_DRVREG_CLASS_NETWORK,
/* cMaxInstances */ /* cMaxInstances */
~0U, ~0U,
/* cbInstance */ /* cbInstance */
sizeof(DRVTAP), sizeof(DRVNIC),
/* pfnConstruct */ /* pfnConstruct */
drvTAPConstruct, drvNicConstruct,
/* pfnDestruct */ /* pfnDestruct */
drvTAPDestruct, drvNicDestruct,
/* pfnRelocate */ /* pfnRelocate */
NULL, NULL,
/* pfnIOCtl */ /* pfnIOCtl */
NULL, NULL,
/* pfnPowerOn */ /* pfnPowerOn */
NULL, NULL,
/* pfnReset */ /* pfnReset */
NULL, NULL,
/* pfnSuspend */ /* pfnSuspend */
NULL, /** @todo Do power on, suspend and resume handlers! */ NULL,
/* pfnResume */ /* pfnResume */
NULL, NULL,
/* pfnAttach */ /* pfnAttach */
NULL, NULL,
/* pfnDetach */ /* pfnDetach */
NULL, NULL,
/* pfnPowerOff */ /* pfnPowerOff */
NULL, NULL,
/* pfnSoftReset */ /* pfnSoftReset */
NULL, NULL,
/* u32EndVersion */ /* u32EndVersion */
PDM_DRVREG_VERSION PDM_DRVREG_VERSION
}; };