}\r
\r
if( query_context.status != IB_SUCCESS ) {\r
- SDP_PRINT(SDP_ERR, SDP_ARP, ("ib_query failed ib_status = 0x%d\n", query_context.status ));\r
+ SDP_PRINT(SDP_ERR, SDP_ARP, ("query_context.status failed ib_status = 0x%d\n", query_context.status ));\r
rc = IB2Status(query_context.status);\r
goto Cleanup;\r
}\r
4 * 256*256 +\r
8 * 256 +\r
+ 120) {\r
- *SrcPortGuid = CL_NTOH64(0x2c90200002002);//????? swlab120\r
- *SrcCaGuid = CL_NTOH64(0x2c90200002000);\r
+// *SrcPortGuid = CL_NTOH64(0x2c90200002001);//????? swlab120\r
+// *SrcCaGuid = CL_NTOH64(0x2c90200002000);\r
+\r
+ *SrcPortGuid = CL_NTOH64(0x2c9010b7c4362);//????? swlab159\r
+ *SrcCaGuid = CL_NTOH64(0x2c9010b7c4360);\r
return STATUS_SUCCESS;\r
}\r
ASSERT(FALSE);\r
m_CurrentlySentBuffers = 0;\r
m_CurrentlyAllocated = 0;\r
m_ClientWaiting = false;\r
+ m_PostCreditsWhenCan = false;\r
+ m_CreditsCurrentlyPosted = false;\r
+ m_CreditdBufferDescriptor = NULL;\r
+\r
}\r
\r
NTSTATUS \r
AssertLocked();\r
\r
NTSTATUS rc = STATUS_SUCCESS;\r
+ ASSERT(pBufferDescriptor->GetFlags() == 0);\r
\r
m_QueuedPackets.InsertTailList(&pBufferDescriptor->BuffersList);\r
rc = SendBuffersIfCan();\r
/*\r
called when a send packet has finished.\r
*/\r
-VOID \r
+\r
+NTSTATUS \r
BufferPool::ReturnBuffer(BufferDescriptor *pBufferDescriptor)\r
{\r
SDP_PRINT(SDP_TRACE, SDP_BUFFER_POOL, ("this = 0x%p buffer=0x%p\n",this, pBufferDescriptor));\r
+ AssertLocked();\r
+ bool CreditUpdate = false;\r
+ NTSTATUS rc = STATUS_SUCCESS;\r
\r
#if DBG \r
if (m_CurrentlySentBuffers == 1) {\r
SDP_PRINT(SDP_WARN, SDP_PERFORMANCE, ("Currently no packets are bing sent m_ClientWaiting = %s\n", \r
m_ClientWaiting ? "true" : "false")); \r
}\r
-#endif \r
- \r
- AssertLocked();\r
- m_FreePackets.InsertTailList(&pBufferDescriptor->BuffersList);\r
- // Is there a client waiting ?\r
- if ( m_ClientWaiting) {\r
- KeSetEvent( &m_WaitingClients, IO_NO_INCREMENT, FALSE );\r
- m_ClientWaiting = false; \r
+#endif\r
+ ASSERT( pBufferDescriptor->GetFlags() == CREDIT_UPDATE ||\r
+ pBufferDescriptor->GetFlags() == 0);\r
+\r
+ CreditUpdate = (pBufferDescriptor->GetFlags() == CREDIT_UPDATE);\r
+ if (CreditUpdate) {\r
+ // This is a credit update packet, need to act accordingly\r
+ ASSERT(m_CreditdBufferDescriptor == NULL);\r
+ ASSERT(m_CreditsCurrentlyPosted == true);\r
+ m_CreditdBufferDescriptor = pBufferDescriptor;\r
+ m_CreditsCurrentlyPosted = false;\r
+ \r
+ if (m_PostCreditsWhenCan == true) {\r
+ m_PostCreditsWhenCan = false;\r
+ rc = PostCredits();\r
+ if (!NT_SUCCESS(rc)) {\r
+ SDP_PRINT(SDP_ERR, SDP_BUFFER_POOL, ("PostCredits failed rc = 0x%x\n", rc ));\r
+ goto Cleanup;\r
+ } \r
+ }\r
+ } else {\r
+ m_FreePackets.InsertTailList(&pBufferDescriptor->BuffersList);\r
+ // Is there a client waiting ?\r
+ if ( m_ClientWaiting) {\r
+ KeSetEvent( &m_WaitingClients, IO_NO_INCREMENT, FALSE );\r
+ m_ClientWaiting = false; \r
+ }\r
}\r
+Cleanup: \r
m_CurrentlySentBuffers--;\r
ASSERT(m_CurrentlySentBuffers >= 0);\r
+\r
+ return rc;\r
}\r
\r
/*\r
BufferDescriptor::DeAllocateBuffer(pBufferDescriptor, SEND_BUFFERS_ALLOCATION_TAG);\r
}\r
\r
+ if(m_CreditdBufferDescriptor != NULL) {\r
+ BufferDescriptor::DeAllocateBuffer(m_CreditdBufferDescriptor, SEND_BUFFERS_ALLOCATION_TAG);\r
+ m_CreditdBufferDescriptor = NULL;\r
+ }\r
+\r
}\r
\r
NTSTATUS\r
\r
msg_hdr_bsdh *pHeader = (msg_hdr_bsdh *) pBufferDescriptor->pBuffer;\r
\r
- pHeader->recv_bufs = m_pSdpSocket->m_RecvBufferPool.GetCurrentlyPostedRecievedBuffers(); //?????recv_bufs = conn->l_advt_bf;\r
+ pHeader->recv_bufs = m_pSdpSocket->m_RecvBufferPool.GetCurrentlyPostedRecievedBuffers();\r
pHeader->size = pBufferDescriptor->DataSize + sizeof msg_hdr_bsdh;\r
pHeader->seq_num = GetAndIncreaseSendSeq();\r
- pHeader->seq_ack = m_pSdpSocket->m_RecvBufferPool.GetRecvSeq();//????conn->advt_seq;\r
+ pHeader->seq_ack = m_pSdpSocket->m_RecvBufferPool.GetRecvSeq();\r
m_AdvtSeq = pHeader->seq_ack;// Currently only for debug\r
pHeader->mid = SDP_MID_DATA;\r
pHeader->flags = SDP_MSG_FLAG_NON_FLAG;\r
ib_send_wr_t send_wr;\r
\r
send_wr.p_next = NULL;\r
- send_wr.wr_id = (uintn_t)pBufferDescriptor;//??? buff->wrid;//?????(uint64_t) (uintptr_t) wr;\r
+ send_wr.wr_id = (uintn_t)pBufferDescriptor;//?????(uint64_t) (uintptr_t) wr;\r
send_wr.wr_type = WR_SEND;\r
send_wr.send_opt = IB_SEND_OPT_SIGNALED;//socket_info->send_opt;\r
\r
}\r
m_CurrentlySentBuffers ++;\r
m_rRecvBuf--;\r
+ m_pSdpSocket->m_RecvBufferPool.UpdateLocaleAdvertisedBuffers();\r
\r
Cleanup:\r
return rc;\r
}\r
\r
+\r
+NTSTATUS \r
+BufferPool::PostCredits()\r
+{\r
+ SDP_PRINT(SDP_TRACE, SDP_BUFFER_POOL, ("this = 0x%p \n",this));\r
+ AssertLocked(); \r
+ NTSTATUS rc = STATUS_SUCCESS;\r
+\r
+ // TODO: If we currently have buffers with data that we should post, and we\r
+ // have enough credits then we shouldn't do anything. data will be sent when\r
+ // the time comes.\r
+\r
+ if (m_CreditsCurrentlyPosted) {\r
+ // We will have to send them once we can\r
+ m_PostCreditsWhenCan = true;\r
+ goto Cleanup;\r
+ }\r
+ \r
+ // Post the credit\r
+ if (m_CreditdBufferDescriptor == NULL) {\r
+\r
+ rc = BufferDescriptor::AllocateBuffer(\r
+ &m_CreditdBufferDescriptor, \r
+ sizeof msg_hdr_bsdh, \r
+ SEND_BUFFERS_ALLOCATION_TAG\r
+ );\r
+ if (!NT_SUCCESS(rc)) {\r
+ SDP_PRINT(SDP_ERR, SDP_BUFFER_POOL, ("AllocateBuffer failed rc = 0x%x\n", rc ));\r
+ ASSERT(m_CreditdBufferDescriptor == NULL);\r
+ goto Cleanup;\r
+ }\r
+ m_CreditdBufferDescriptor->SetFlags(CREDIT_UPDATE);\r
+ }\r
+ ASSERT(m_CreditdBufferDescriptor->GetFlags() == CREDIT_UPDATE);\r
+\r
+ ASSERT(m_CreditdBufferDescriptor->DataSize == 0);\r
+\r
+ rc = SendBuffer(m_CreditdBufferDescriptor);\r
+ if (!NT_SUCCESS(rc)) {\r
+ SDP_PRINT(SDP_ERR, SDP_BUFFER_POOL, ("AllocateBuffer failed rc = 0x%x\n", rc ));\r
+ goto Cleanup;\r
+ }\r
+ \r
+ m_CreditsCurrentlyPosted = true;\r
+ m_CreditdBufferDescriptor = NULL;\r
+ \r
+Cleanup:\r
+ return rc;\r
+\r
+}\r
+\r
VOID \r
BufferPool::AssertLocked() {\r
#if DBG\r
// The defenition of the function that we use to report back errors\r
typedef void (* SendErrorCB )(NTSTATUS Error, VOID *Context);\r
\r
+// The flags that are being used to give more information about the BufferDescriptors\r
+const uint8_t CREDIT_UPDATE = 1;\r
+\r
\r
// Each buffer starts with msg_hdr_bsdh and is followed by the actual data\r
class BufferDescriptor {\r
VOID Reset() {\r
DataSize = 0;\r
DataStart = 0;\r
+ Flags = 0;\r
}\r
\r
+ VOID SetFlags(uint8_t flags) { Flags = flags; }\r
+ uint8_t GetFlags() { return Flags; }\r
+\r
// Each buffer starts with bsdh_hdr structure\r
- VOID *pBuffer; // A pointer to the actual place that we put the data\r
- uint32_t BufferSize; // The total size of the buffer (size that we have allocated)\r
- uint32_t DataSize; // The size of the data\r
- uint32_t DataStart; // The place in which the data starts (used for recieve packets)\r
- LIST_ENTRY BuffersList; // The place to hold the list of the buffers\r
+ VOID *pBuffer; // A pointer to the actual place that we put the data\r
+ uint32_t BufferSize; // The total size of the buffer (size that we have allocated)\r
+ uint32_t DataSize; // The size of the data\r
+ uint32_t DataStart; // The place in which the data starts (used for recieve packets)\r
+ LIST_ENTRY BuffersList; // The place to hold the list of the buffers\r
+ uint8_t Flags; // A field that tells if there is anything special in this descriptor\r
\r
ib_local_ds_t ds_array; // Used for sending the buffer\r
\r
\r
VOID AllowOthersToGet(); \r
\r
- VOID ReturnBuffer(BufferDescriptor *pBufferDescriptor);\r
+ NTSTATUS ReturnBuffer(BufferDescriptor *pBufferDescriptor);\r
\r
NTSTATUS SendBuffersIfCan();\r
\r
if (m_rRecvBuf == 2) {\r
SDP_PRINT(SDP_ALL, SDP_BUFFER_POOL,("m_rRecvBuf = %d, it is being set to %d seqnum = %d\n", m_rRecvBuf, rRecvBuf, m_SendSeq));\r
}\r
+ ASSERT(rRecvBuf < 1000);\r
m_rRecvBuf = rRecvBuf;\r
}\r
\r
-\r
+ NTSTATUS PostCredits();\r
\r
private:\r
\r
\r
SdpSocket *m_pSdpSocket;\r
\r
+ /*\r
+ Following two flags are responsible for sending the credits to the \r
+ remote side.\r
+ Since we are not allowed to send more than one more credit at a time, \r
+ we have to remember if a credit is being sent, and if one is, we have\r
+ to delay the send of the next credit to the time that the previous \r
+ credit was sent\r
+ */\r
+ bool m_PostCreditsWhenCan;\r
+ bool m_CreditsCurrentlyPosted;\r
+ BufferDescriptor *m_CreditdBufferDescriptor;\r
+\r
VOID AssertLocked();\r
\r
};\r
NTSTATUS rc = STATUS_SUCCESS;\r
ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);\r
bool Locked = false;\r
+ bool WaitedOnLock = false;\r
do {\r
KeAcquireSpinLock(&m_SpinLock, &OldIrql);\r
\r
if (m_InUse) {\r
// We have to release the spinlock and wait on the event\r
- m_NumberOfClientWaiting++;\r
+ if (WaitedOnLock == false) {\r
+ m_NumberOfClientWaiting++;\r
+ WaitedOnLock = true;\r
+ }\r
KeReleaseSpinLock(&m_SpinLock, OldIrql);\r
rc = MyKeWaitForSingleObject(&m_Event, UserRequest, UserMode, false, NULL);\r
if (( rc == STATUS_ALERTED ) ||( rc == STATUS_USER_APC )) {\r
KeClearEvent(&m_Event);\r
OldFlags = m_flags;\r
ResetFlags(m_flags);\r
- m_NumberOfClientWaiting--;\r
+ if (WaitedOnLock) {\r
+ m_NumberOfClientWaiting--;\r
+ }\r
+ ASSERT(m_NumberOfClientWaiting >= 0);\r
KeReleaseSpinLock(&m_SpinLock, OldIrql);\r
rc = HandleFlags(OldFlags);\r
if (!NT_SUCCESS(rc)) {\r
m_ClientBeingServed = false;\r
m_CurrentlyPostedRecievedBuffers = 0;\r
m_CurrentlyAllocated = 0;\r
- m_ClientWaiting = false; \r
+ m_ClientWaiting = false;\r
+ m_LocaleAdvertisedBuffers = 0;\r
}\r
\r
NTSTATUS \r
}\r
// ???? Handle state changes here ????\r
\r
+ // Check if we have to send more credits to the remote side\r
+ ASSERT(m_LocaleAdvertisedBuffers > 0);\r
+ m_LocaleAdvertisedBuffers--;\r
+\r
+ ASSERT(m_CurrentlyPostedRecievedBuffers >= m_LocaleAdvertisedBuffers);\r
+ if (m_CurrentlyPostedRecievedBuffers - m_LocaleAdvertisedBuffers > \r
+ SDP_RECV_CREDIT_UPDATE) {\r
+ rc = m_pSdpSocket->m_SendBufferPool.PostCredits();\r
+ if (!NT_SUCCESS(rc)) {\r
+ SDP_PRINT(SDP_ERR, SDP_BUFFER_POOL, ("m_SendBufferPool.PostCredits failed rc = 0x%x\n", rc ));\r
+ goto Cleanup;\r
+ } \r
+ }\r
+\r
// Wake up the clients of the send (new credits were added)\r
rc = m_pSdpSocket->m_SendBufferPool.SendBuffersIfCan();\r
if (!NT_SUCCESS(rc)) {\r
} \r
\r
m_CurrentlyPostedRecievedBuffers--;\r
+ \r
ASSERT(m_CurrentlyPostedRecievedBuffers >= 0);\r
// We might be able to post a new recieve buffer now\r
ASSERT(m_CurrentlyPostedRecievedBuffers < m_MaxConcurrentRecieves);\r
if (!NT_SUCCESS(rc)) {\r
SDP_PRINT(SDP_ERR, SDP_BUFFER_POOL, ("RecieveIfCan failed rc = 0x%x\n", rc ));\r
goto Cleanup;\r
- } \r
+ }\r
\r
Cleanup: \r
return rc;\r
NTSTATUS \r
RecvPool::ReceiveIfCan()\r
{\r
- SDP_PRINT(SDP_TRACE, SDP_BUFFER_POOL, ("this = 0x%p \n",this));\r
+ SDP_PRINT(SDP_TRACE, SDP_BUFFER_POOL, ("this = 0x%p m_CurrentlyPostedRecievedBuffers = %d m_LocaleAdvertisedBuffers = %d\n",\r
+ this, m_CurrentlyPostedRecievedBuffers, m_LocaleAdvertisedBuffers));\r
AssertLocked();\r
BufferDescriptor *pBufferDescriptor = NULL;\r
NTSTATUS rc = STATUS_SUCCESS;\r
+ uint16_t StartLocaleAdvertisedBuffers = m_LocaleAdvertisedBuffers;\r
\r
while (m_CurrentlyPostedRecievedBuffers < m_MaxConcurrentRecieves) {\r
// do we have a free packet ?\r
goto Cleanup;\r
} \r
}\r
+ if (StartLocaleAdvertisedBuffers <= 2) {\r
+ // In this case we have (probably) posted some buffers. We have to notify the\r
+ // other side that he has more credits\r
+ rc = m_pSdpSocket->m_SendBufferPool.PostCredits();\r
+ if (!NT_SUCCESS(rc)) {\r
+ SDP_PRINT(SDP_ERR, SDP_BUFFER_POOL, ("m_SendBufferPool.PostCredits failed rc = 0x%x\n", rc ));\r
+ goto Cleanup;\r
+ } \r
+ }\r
\r
Cleanup:\r
return rc;\r
\r
VOID ShutDown();\r
\r
- uint16_t GetCurrentlyPostedRecievedBuffers(){return m_CurrentlyPostedRecievedBuffers;}\r
- \r
+ uint16_t GetCurrentlyPostedRecievedBuffers() {\r
+ return m_CurrentlyPostedRecievedBuffers;\r
+ }\r
+\r
+ void SetLocaleAdvertisedBuffers(uint16_t LocaleAdvertisedBuffers) {\r
+ m_LocaleAdvertisedBuffers = LocaleAdvertisedBuffers;\r
+ }\r
+\r
+ void UpdateLocaleAdvertisedBuffers() {\r
+ m_LocaleAdvertisedBuffers = m_CurrentlyPostedRecievedBuffers;\r
+ }\r
private:\r
\r
NTSTATUS PostReceiveBuffer(BufferDescriptor *pBufferDescriptor);\r
\r
// Global data about this connection\r
int m_MaxBuffers; // The maximum number of buffers that we allow for this QP (to be allocated)\r
- int m_MaxConcurrentRecieves; // The total numbers of sends that are allowd for the QP\r
+ int m_MaxConcurrentRecieves; // The total numbers of recvs that are allowd for the QP\r
int m_MaxMessageSize; // The maximum buffer size that we allow for recieving\r
\r
uint16_t m_CurrentlyPostedRecievedBuffers; // Number of buffers that we have posted for recieve and didn't get an answer yet\r
+ uint16_t m_LocaleAdvertisedBuffers; // Number of buffers that we have advertised to the remote side (l_advt_bf in linux)\r
+ \r
int m_CurrentlyAllocated; // The number of buffers that we have already allocated\r
\r
bool m_ClientBeingServed; // true if we have already started giving buffers to a client\r
pSocket->CmRepCallback(p_cm_rep_rec);\r
}\r
\r
-static void AL_API\r
-cm_req_callback(\r
- IN ib_cm_req_rec_t *p_cm_req_rec )\r
-{\r
- SDP_PRINT(SDP_TRACE, SDP_SOCKET, ("cm_req_callback called"));\r
- ASSERT(FALSE);\r
-}\r
-\r
static void AL_API\r
cm_mra_callback(\r
IN ib_cm_mra_rec_t *p_cm_mra_rec )\r
{ \r
SDP_PRINT(SDP_TRACE, SDP_SOCKET, ("this = 0x%p \n",this));\r
\r
- NTSTATUS rc = STATUS_SUCCESS;\r
+ NTSTATUS rc = STATUS_SUCCESS; \r
+ NTSTATUS rc1; // used only to check that there are no more errors on the \r
+ // return path\r
BufferDescriptor * pBufferDescriptor = NULL;\r
bool First = true;\r
ULONG Coppied = 0;\r
if (!NT_SUCCESS(rc)) {\r
SDP_PRINT(SDP_ERR, SDP_SOCKET, ("pBufferDescriptor->WriteData failed rc = 0x%x\n", rc ));\r
// free the buffer that you have\r
- m_SendBufferPool.ReturnBuffer(pBufferDescriptor);\r
+ rc1 = m_SendBufferPool.ReturnBuffer(pBufferDescriptor);\r
+ ASSERT(NT_SUCCESS(rc1));\r
m_Lock.Unlock(); // Error ignored as this is already an error pass \r
goto Cleanup;\r
}\r
if (!NT_SUCCESS(rc)) {\r
SDP_PRINT(SDP_ERR, SDP_SOCKET, ("m_SendBufferPool.AddBufferToQueuedList failed rc = 0x%x\n", rc ));\r
// free the buffer that you have\r
- m_SendBufferPool.ReturnBuffer(pBufferDescriptor);\r
+ rc1 = m_SendBufferPool.ReturnBuffer(pBufferDescriptor);\r
+ ASSERT(NT_SUCCESS(rc1));\r
m_Lock.Unlock(); // Error ignored as this is already an error pass \r
goto Cleanup;\r
}\r
// We need to prepare the hello mesage for the CM\r
sdp_msg_hello hello_msg;\r
CreateHelloHeader(&hello_msg, pWspConnectIn->IP);\r
+ // We can now update the number of buffers that we have\r
+ m_RecvBufferPool.SetLocaleAdvertisedBuffers(CL_NTOH16(hello_msg.bsdh.recv_bufs));\r
\r
// Create the CM request\r
ib_cm_req_t cm_req;\r
\r
ib_status = ib_cm_req( &cm_req );\r
if( ib_status != IB_SUCCESS ) {\r
- SDP_PRINT(SDP_ERR, SDP_SOCKET, ("ib_open_ca failed ib_status = 0x%d\n", ib_status ));\r
+ SDP_PRINT(SDP_ERR, SDP_SOCKET, ("ib_cm_req failed ib_status = 0x%d\n", ib_status ));\r
rc = IB2Status(ib_status);\r
pWspConnectOut->Errno = WSAENETUNREACH; // BUGBUG: verify this error\r
goto Cleanup;\r
NTSTATUS SdpSocket::send_cb()\r
{\r
SDP_PRINT(SDP_TRACE, SDP_SOCKET, ("called this =0x%x\n", this));\r
- NTSTATUS rc = STATUS_SUCCESS;\r
+ NTSTATUS rc = STATUS_SUCCESS, rc1 = STATUS_SUCCESS, rc2 = STATUS_SUCCESS;\r
ib_api_status_t ib_status;\r
ib_wc_t *p_wc, *p_free;\r
size_t i;\r
{\r
ASSERT( p_wc->wc_type == IB_WC_SEND );\r
pBufferDescriptor = (BufferDescriptor*)(uintn_t)p_wc->wr_id;\r
- m_SendBufferPool.ReturnBuffer(pBufferDescriptor);\r
- \r
+ rc2 = m_SendBufferPool.ReturnBuffer(pBufferDescriptor);\r
+ UpdateRc(&rc1, rc2); // We remember the error here and continue to avoid leaks\r
switch( p_wc->status )\r
{\r
case IB_WCS_SUCCESS:\r
}\r
/* If we didn't use up every WC, break out. */\r
} while( !p_free );\r
-\r
+ if (!NT_SUCCESS(rc1)) {\r
+ SDP_PRINT(SDP_ERR, SDP_SOCKET, ("Error was detected on ReturnBuffer rc1 = 0x%x\n", rc1 ));\r
+ ASSERT(NT_SUCCESS(rc));\r
+ rc = rc1;\r
+ goto Cleanup;\r
+ }\r
+ \r
\r
/* Rearm the CQ. */\r
ib_status = ib_rearm_cq(m_scq, FALSE );\r
cm_req->rnr_retry_cnt = 6;//????QP_ATTRIB_RNR_RETRY;\r
cm_req->retry_cnt = 6;//????QP_ATTRIB_RETRY_COUNT;\r
\r
- cm_req->pfn_cm_req_cb = cm_req_callback;\r
+ cm_req->pfn_cm_req_cb = NULL;\r
cm_req->pfn_cm_mra_cb = cm_mra_callback;\r
cm_req->pfn_cm_rej_cb = cm_rej_callback;\r
cm_req->pfn_cm_rep_cb = cm_rep_callback;\r
*/\r
#define QP_ATTRIB_RQ_DEPTH 64\r
#define QP_ATTRIB_RQ_SGE 1\r
+const int SDP_RECV_CREDIT_UPDATE = 20; // send credit update to the remote \r
+ // side.\r
\r
/* Number of entries in a CQ */\r
#define IB_CQ_SIZE (QP_ATTRIB_SQ_DEPTH + QP_ATTRIB_RQ_DEPTH + 1)\r
\r
recv:\r
1) What to do when I don\92t have all the buffer to return?\r
+ 2) When posting the credits, consider not sending the credits if there are packets pending and \r
+ and we have credits.\r
\r
general:\r
\r
\r
USER MODE:\r
\r
+* check why DHCP doesn't work when the provider is installed.\r
\r
* Check the lifetime of the SdpSocket (when is it deleted and so)??\r
\r