VirtualBox

source: vbox/trunk/src/VBox/GuestHost/OpenGL/util/net.c@ 71930

Last change on this file since 71930 was 69392, checked in by vboxsync, 7 years ago

GuestHost/OpenGL: scm updates

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 37.1 KB
Line 
1/* Copyright (c) 2001, Stanford University
2 * All rights reserved
3 *
4 * See the file LICENSE.txt for information on redistributing this software.
5 */
6
7#include <stdio.h>
8#include <stdlib.h>
9#include <stdarg.h>
10#include <errno.h>
11#include <memory.h>
12#include <signal.h>
13
14#ifdef WINDOWS
15#define WIN32_LEAN_AND_MEAN
16#include <process.h>
17#else
18#include <unistd.h>
19#endif
20
21#include "cr_mem.h"
22#include "cr_error.h"
23#include "cr_string.h"
24#include "cr_url.h"
25#include "cr_net.h"
26#include "cr_netserver.h"
27#include "cr_pixeldata.h"
28#include "cr_environment.h"
29#include "cr_endian.h"
30#include "cr_bufpool.h"
31#include "cr_threads.h"
32#include "net_internals.h"
33
34
35#define CR_MINIMUM_MTU 1024
36
37#define CR_INITIAL_RECV_CREDITS ( 1 << 21 ) /* 2MB */
38
39/* Allow up to four processes per node. . . */
40#define CR_QUADRICS_LOWEST_RANK 0
41#define CR_QUADRICS_HIGHEST_RANK 3
42
43static struct {
44 int initialized; /* flag */
45 CRNetReceiveFuncList *recv_list; /* what to do with arriving packets */
46 CRNetCloseFuncList *close_list; /* what to do when a client goes down */
47
48 /* Number of connections using each type of interface: */
49 int use_tcpip;
50 int use_ib;
51 int use_file;
52 int use_udp;
53 int use_gm;
54 int use_sdp;
55 int use_teac;
56 int use_tcscomm;
57 int use_hgcm;
58
59 int num_clients; /* total number of clients (unused?) */
60
61#ifdef CHROMIUM_THREADSAFE
62 CRmutex mutex;
63#endif
64 int my_rank; /* Teac/TSComm only */
65} cr_net;
66
67
68
69/**
70 * Helper routine used by both crNetConnectToServer() and crNetAcceptClient().
71 * Call the protocol-specific Init() and Connection() functions.
72 *
73 */
74static void
75InitConnection(CRConnection *conn, const char *protocol, unsigned int mtu
76#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
77 , struct VBOXUHGSMI *pHgsmi
78#endif
79 )
80{
81 if (!crStrcmp(protocol, "devnull"))
82 {
83 crDevnullInit(cr_net.recv_list, cr_net.close_list, mtu);
84 crDevnullConnection(conn);
85 }
86 else if (!crStrcmp(protocol, "file"))
87 {
88 cr_net.use_file++;
89 crFileInit(cr_net.recv_list, cr_net.close_list, mtu);
90 crFileConnection(conn);
91 }
92 else if (!crStrcmp(protocol, "swapfile"))
93 {
94 /* file with byte-swapping */
95 cr_net.use_file++;
96 crFileInit(cr_net.recv_list, cr_net.close_list, mtu);
97 crFileConnection(conn);
98 conn->swap = 1;
99 }
100 else if (!crStrcmp(protocol, "tcpip"))
101 {
102 cr_net.use_tcpip++;
103 crTCPIPInit(cr_net.recv_list, cr_net.close_list, mtu);
104 crTCPIPConnection(conn);
105 }
106 else if (!crStrcmp(protocol, "udptcpip"))
107 {
108 cr_net.use_udp++;
109 crUDPTCPIPInit(cr_net.recv_list, cr_net.close_list, mtu);
110 crUDPTCPIPConnection(conn);
111 }
112#ifdef VBOX_WITH_HGCM
113 else if (!crStrcmp(protocol, "vboxhgcm"))
114 {
115 cr_net.use_hgcm++;
116 crVBoxHGCMInit(cr_net.recv_list, cr_net.close_list, mtu);
117 crVBoxHGCMConnection(conn
118#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
119 , pHgsmi
120#endif
121 );
122 }
123#endif
124#ifdef GM_SUPPORT
125 else if (!crStrcmp(protocol, "gm"))
126 {
127 cr_net.use_gm++;
128 crGmInit(cr_net.recv_list, cr_net.close_list, mtu);
129 crGmConnection(conn);
130 }
131#endif
132#ifdef TEAC_SUPPORT
133 else if (!crStrcmp(protocol, "quadrics"))
134 {
135 cr_net.use_teac++;
136 crTeacInit(cr_net.recv_list, cr_net.close_list, mtu);
137 crTeacConnection(conn);
138 }
139#endif
140#ifdef TCSCOMM_SUPPORT
141 else if (!crStrcmp(protocol, "quadrics-tcscomm"))
142 {
143 cr_net.use_tcscomm++;
144 crTcscommInit(cr_net.recv_list, cr_net.close_list, mtu);
145 crTcscommConnection(conn);
146 }
147#endif
148#ifdef SDP_SUPPORT
149 else if (!crStrcmp(protocol, "sdp"))
150 {
151 cr_net.use_sdp++;
152 crSDPInit(cr_net.recv_list, cr_net.close_list, mtu);
153 crSDPConnection(conn);
154 }
155#endif
156#ifdef IB_SUPPORT
157 else if (!crStrcmp(protocol, "ib"))
158 {
159 cr_net.use_ib++;
160 crDebug("Calling crIBInit()");
161 crIBInit(cr_net.recv_list, cr_net.close_list, mtu);
162 crIBConnection(conn);
163 crDebug("Done Calling crIBInit()");
164 }
165#endif
166#ifdef HP_MULTICAST_SUPPORT
167 else if (!crStrcmp(protocol, "hpmc"))
168 {
169 cr_net.use_hpmc++;
170 crHPMCInit(cr_net.recv_list, cr_net.close_list, mtu);
171 crHPMCConnection(conn);
172 }
173#endif
174 else
175 {
176 crError("Unknown protocol: \"%s\"", protocol);
177 }
178}
179
180
181
182/**
183 * Establish a connection with a server.
184 * \param server the server to connect to, in the form
185 * "protocol://servername:port" where the port specifier
186 * is optional and if the protocol is missing it is assumed
187 * to be "tcpip".
188 * \param default_port the port to connect to, if port not specified in the
189 * server URL string.
190 * \param mtu desired maximum transmission unit size (in bytes)
191 * \param broker either 1 or 0 to indicate if connection is brokered through
192 * the mothership
193 */
194CRConnection * crNetConnectToServer( const char *server, unsigned short default_port, int mtu, int broker
195#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
196 , struct VBOXUHGSMI *pHgsmi
197#endif
198)
199{
200 char hostname[4096], protocol[4096];
201 unsigned short port;
202 CRConnection *conn;
203
204 crDebug( "In crNetConnectToServer( \"%s\", port=%d, mtu=%d, broker=%d )",
205 server, default_port, mtu, broker );
206
207 CRASSERT( cr_net.initialized );
208
209 if (mtu < CR_MINIMUM_MTU)
210 {
211 crError( "You tried to connect to server \"%s\" with an mtu of %d, "
212 "but the minimum MTU is %d", server, mtu, CR_MINIMUM_MTU );
213 }
214
215 /* Tear the URL apart into relevant portions. */
216 if ( !crParseURL( server, protocol, hostname, &port, default_port ) ) {
217 crError( "Malformed URL: \"%s\"", server );
218 }
219
220 /* If the host name is "localhost" replace it with the _real_ name
221 * of the localhost. If we don't do this, there seems to be
222 * confusion in the mothership as to whether or not "localhost" and
223 * "foo.bar.com" are the same machine.
224 */
225 if (crStrcmp(hostname, "localhost") == 0) {
226 int rv = crGetHostname(hostname, 4096);
227 CRASSERT(rv == 0);
228 (void) rv;
229 }
230
231 /* XXX why is this here??? I think it could be moved into the
232 * crTeacConnection() function with no problem. I.e. change the
233 * connection's port, teac_rank and tcscomm_rank there. (BrianP)
234 */
235 if ( !crStrcmp( protocol, "quadrics" ) ||
236 !crStrcmp( protocol, "quadrics-tcscomm" ) ) {
237 /* For Quadrics protocols, treat "port" as "rank" */
238 if ( port > CR_QUADRICS_HIGHEST_RANK ) {
239 crWarning( "Invalid crserver rank, %d, defaulting to %d\n",
240 port, CR_QUADRICS_LOWEST_RANK );
241 port = CR_QUADRICS_LOWEST_RANK;
242 }
243 }
244 crDebug( "Connecting to %s on port %d, with protocol %s",
245 hostname, port, protocol );
246
247#ifdef SDP_SUPPORT
248 /* This makes me ill, but we need to "fix" the hostname for sdp. MCH */
249 if (!crStrcmp(protocol, "sdp")) {
250 char* temp;
251 temp = strtok(hostname, ".");
252 crStrcat(temp, crGetSDPHostnameSuffix());
253 crStrcpy(hostname, temp);
254 crDebug("SDP rename hostname: %s", hostname);
255 }
256#endif
257
258 conn = (CRConnection *) crCalloc( sizeof(*conn) );
259 if (!conn)
260 return NULL;
261
262 /* init the non-zero fields */
263 conn->type = CR_NO_CONNECTION; /* we don't know yet */
264 conn->recv_credits = CR_INITIAL_RECV_CREDITS;
265 conn->hostname = crStrdup( hostname );
266 conn->port = port;
267 conn->mtu = mtu;
268 conn->buffer_size = mtu;
269 conn->broker = broker;
270 conn->endianness = crDetermineEndianness();
271 /* XXX why are these here??? Move them into the crTeacConnection()
272 * and crTcscommConnection() functions.
273 */
274 conn->teac_id = -1;
275 conn->teac_rank = port;
276 conn->tcscomm_id = -1;
277 conn->tcscomm_rank = port;
278
279 crInitMessageList(&conn->messageList);
280
281 /* now, just dispatch to the appropriate protocol's initialization functions. */
282 InitConnection(conn, protocol, mtu
283#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
284 , pHgsmi
285#endif
286 );
287
288 if (!crNetConnect( conn ))
289 {
290 crDebug("crNetConnectToServer() failed, freeing the connection");
291 #ifdef CHROMIUM_THREADSAFE
292 crFreeMutex( &conn->messageList.lock );
293 #endif
294 conn->Disconnect(conn);
295 crFree( conn );
296 return NULL;
297 }
298
299 crDebug( "Done connecting to %s (swapping=%d)", server, conn->swap );
300 return conn;
301}
302
303/**
304 * Send a message to the receiver that another connection is needed.
305 * We send a CR_MESSAGE_NEWCLIENT packet, then call crNetServerConnect.
306 */
307void crNetNewClient( CRNetServer *ns
308#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
309 , struct VBOXUHGSMI *pHgsmi
310#endif
311)
312{
313 /*
314 unsigned int len = sizeof(CRMessageNewClient);
315 CRMessageNewClient msg;
316
317 CRASSERT( conn );
318
319 if (conn->swap)
320 msg.header.type = (CRMessageType) SWAP32(CR_MESSAGE_NEWCLIENT);
321 else
322 msg.header.type = CR_MESSAGE_NEWCLIENT;
323
324 crNetSend( conn, NULL, &msg, len );
325 */
326
327 crNetServerConnect( ns
328#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
329 , pHgsmi
330#endif
331);
332}
333
334
335/**
336 * Accept a connection from a client.
337 * \param protocol the protocol to use (such as "tcpip" or "gm")
338 * \param hostname optional hostname of the expected client (may be NULL)
339 * \param port number of the port to accept on
340 * \param mtu maximum transmission unit
341 * \param broker either 1 or 0 to indicate if connection is brokered through
342 * the mothership
343 * \return new CRConnection object, or NULL
344 */
345CRConnection *
346crNetAcceptClient( const char *protocol, const char *hostname,
347 unsigned short port, unsigned int mtu, int broker )
348{
349 CRConnection *conn;
350
351 CRASSERT( cr_net.initialized );
352
353 conn = (CRConnection *) crCalloc( sizeof( *conn ) );
354 if (!conn)
355 return NULL;
356
357 /* init the non-zero fields */
358 conn->type = CR_NO_CONNECTION; /* we don't know yet */
359 conn->recv_credits = CR_INITIAL_RECV_CREDITS;
360 conn->port = port;
361 conn->mtu = mtu;
362 conn->buffer_size = mtu;
363 conn->broker = broker;
364 conn->endianness = crDetermineEndianness();
365 conn->teac_id = -1;
366 conn->teac_rank = -1;
367 conn->tcscomm_id = -1;
368 conn->tcscomm_rank = -1;
369
370 crInitMessageList(&conn->messageList);
371
372 /* now, just dispatch to the appropriate protocol's initialization functions. */
373 crDebug("In crNetAcceptClient( protocol=\"%s\" port=%d mtu=%d )",
374 protocol, (int) port, (int) mtu);
375
376 /* special case */
377 if ( !crStrncmp( protocol, "file", crStrlen( "file" ) ) ||
378 !crStrncmp( protocol, "swapfile", crStrlen( "swapfile" ) ) )
379 {
380 char filename[4096];
381 char protocol_only[4096];
382
383 cr_net.use_file++;
384 if (!crParseURL(protocol, protocol_only, filename, NULL, 0))
385 {
386 crError( "Malformed URL: \"%s\"", protocol );
387 }
388 conn->hostname = crStrdup( filename );
389
390 /* call the protocol-specific init routines */ /* ktd (add) */
391 InitConnection(conn, protocol_only, mtu
392#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
393 , NULL
394#endif
395 ); /* ktd (add) */
396 }
397 else {
398 /* call the protocol-specific init routines */
399 InitConnection(conn, protocol, mtu
400#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
401 , NULL
402#endif
403 );
404 }
405
406 crNetAccept( conn, hostname, port );
407 return conn;
408}
409
410
411/**
412 * Close and free given connection.
413 */
414void
415crNetFreeConnection(CRConnection *conn)
416{
417 conn->Disconnect(conn);
418 crFree( conn->hostname );
419 #ifdef CHROMIUM_THREADSAFE
420 crFreeMutex( &conn->messageList.lock );
421 #endif
422 crFree(conn);
423}
424
425
426/**
427 * Start the ball rolling. give functions to handle incoming traffic
428 * (usually placing blocks on a queue), and a handler for dropped
429 * connections.
430 */
431void crNetInit( CRNetReceiveFunc recvFunc, CRNetCloseFunc closeFunc )
432{
433 CRNetReceiveFuncList *rfl;
434 CRNetCloseFuncList *cfl;
435
436 if ( cr_net.initialized )
437 {
438 /*crDebug( "Networking already initialized!" );*/
439 }
440 else
441 {
442#ifdef WINDOWS
443 /** @todo do we actually need that WSA stuff with VBox at all? */
444 WORD wVersionRequested = MAKEWORD(2, 0);
445 WSADATA wsaData;
446 int err;
447
448 err = WSAStartup(wVersionRequested, &wsaData);
449 if (err != 0)
450 crError("Couldn't initialize sockets on WINDOWS");
451#endif
452
453 cr_net.use_gm = 0;
454 cr_net.use_udp = 0;
455 cr_net.use_tcpip = 0;
456 cr_net.use_sdp = 0;
457 cr_net.use_tcscomm = 0;
458 cr_net.use_teac = 0;
459 cr_net.use_file = 0;
460 cr_net.use_hgcm = 0;
461 cr_net.num_clients = 0;
462#ifdef CHROMIUM_THREADSAFE
463 crInitMutex(&cr_net.mutex);
464#endif
465
466 cr_net.initialized = 1;
467 cr_net.recv_list = NULL;
468 cr_net.close_list = NULL;
469 }
470
471 if (recvFunc != NULL)
472 {
473 /* check if function is already in the list */
474 for (rfl = cr_net.recv_list ; rfl ; rfl = rfl->next )
475 {
476 if (rfl->recv == recvFunc)
477 {
478 /* we've already seen this function -- do nothing */
479 break;
480 }
481 }
482 /* not in list, so insert at the head */
483 if (!rfl)
484 {
485 rfl = (CRNetReceiveFuncList *) crAlloc( sizeof (*rfl ));
486 rfl->recv = recvFunc;
487 rfl->next = cr_net.recv_list;
488 cr_net.recv_list = rfl;
489 }
490 }
491
492 if (closeFunc != NULL)
493 {
494 /* check if function is already in the list */
495 for (cfl = cr_net.close_list ; cfl ; cfl = cfl->next )
496 {
497 if (cfl->close == closeFunc)
498 {
499 /* we've already seen this function -- do nothing */
500 break;
501 }
502 }
503 /* not in list, so insert at the head */
504 if (!cfl)
505 {
506 cfl = (CRNetCloseFuncList *) crAlloc( sizeof (*cfl ));
507 cfl->close = closeFunc;
508 cfl->next = cr_net.close_list;
509 cr_net.close_list = cfl;
510 }
511 }
512}
513
514/* Free up stuff */
515void crNetTearDown(void)
516{
517 CRNetReceiveFuncList *rfl;
518 CRNetCloseFuncList *cfl;
519 void *tmp;
520
521 if (!cr_net.initialized) return;
522
523#ifdef CHROMIUM_THREADSAFE
524 crLockMutex(&cr_net.mutex);
525#endif
526
527 /* Note, other protocols used by chromium should free up stuff too,
528 * but VBox doesn't use them, so no other checks.
529 */
530 if (cr_net.use_hgcm)
531 crVBoxHGCMTearDown();
532
533 for (rfl = cr_net.recv_list ; rfl ; rfl = (CRNetReceiveFuncList *) tmp )
534 {
535 tmp = rfl->next;
536 crFree(rfl);
537 }
538
539 for (cfl = cr_net.close_list ; cfl ; cfl = (CRNetCloseFuncList *) tmp )
540 {
541 tmp = cfl->next;
542 crFree(cfl);
543 }
544
545 cr_net.initialized = 0;
546
547#ifdef CHROMIUM_THREADSAFE
548 crUnlockMutex(&cr_net.mutex);
549 crFreeMutex(&cr_net.mutex);
550#endif
551}
552
553CRConnection** crNetDump( int* num )
554{
555 CRConnection **c;
556
557 c = crTCPIPDump( num );
558 if ( c ) return c;
559
560 c = crDevnullDump( num );
561 if ( c ) return c;
562
563 c = crFileDump( num );
564 if ( c ) return c;
565
566#ifdef VBOX_WITH_HGCM
567 c = crVBoxHGCMDump( num );
568 if ( c ) return c;
569#endif
570#ifdef GM_SUPPORT
571 c = crGmDump( num );
572 if ( c ) return c;
573#endif
574#ifdef IB_SUPPORT
575 c = crIBDump( num );
576 if ( c ) return c;
577#endif
578#ifdef SDP_SUPPORT
579 c = crSDPDump( num );
580 if ( c ) return c;
581#endif
582
583 *num = 0;
584 return NULL;
585}
586
587
588/*
589 * Allocate a network data buffer. The size will be the mtu size specified
590 * earlier to crNetConnectToServer() or crNetAcceptClient().
591 *
592 * Buffers that will eventually be transmitted on a connection
593 * *must* be allocated using this interface. This way, we can
594 * automatically pin memory and tag blocks, and we can also use
595 * our own buffer pool management.
596 */
597void *crNetAlloc( CRConnection *conn )
598{
599 CRASSERT( conn );
600 return conn->Alloc( conn );
601}
602
603
604/**
605 * This returns a buffer (which was obtained from crNetAlloc()) back
606 * to the network layer so that it may be reused.
607 */
608void crNetFree( CRConnection *conn, void *buf )
609{
610 conn->Free( conn, buf );
611}
612
613
614void
615crInitMessageList(CRMessageList *list)
616{
617 list->head = list->tail = NULL;
618 list->numMessages = 0;
619#ifdef CHROMIUM_THREADSAFE
620 crInitMutex(&list->lock);
621 crInitCondition(&list->nonEmpty);
622#endif
623}
624
625
626/**
627 * Add a message node to the end of the message list.
628 * \param list the message list
629 * \param msg points to start of message buffer
630 * \param len length of message, in bytes
631 * \param conn connection associated with message (may be NULL)
632 */
633void
634crEnqueueMessage(CRMessageList *list, CRMessage *msg, unsigned int len,
635 CRConnection *conn)
636{
637 CRMessageListNode *node;
638
639#ifdef CHROMIUM_THREADSAFE
640 crLockMutex(&list->lock);
641#endif
642
643 node = (CRMessageListNode *) crAlloc(sizeof(CRMessageListNode));
644 node->mesg = msg;
645 node->len = len;
646 node->conn = conn;
647 node->next = NULL;
648
649 /* insert at tail */
650 if (list->tail)
651 list->tail->next = node;
652 else
653 list->head = node;
654 list->tail = node;
655
656 list->numMessages++;
657
658#ifdef CHROMIUM_THREADSAFE
659 crSignalCondition(&list->nonEmpty);
660 crUnlockMutex(&list->lock);
661#endif
662}
663
664
665/**
666 * Remove first message node from message list and return it.
667 * Don't block.
668 * \return 1 if message was dequeued, 0 otherwise.
669 */
670static int
671crDequeueMessageNoBlock(CRMessageList *list, CRMessage **msg,
672 unsigned int *len, CRConnection **conn)
673{
674 int retval;
675
676#ifdef CHROMIUM_THREADSAFE
677 crLockMutex(&list->lock);
678#endif
679
680 if (list->head) {
681 CRMessageListNode *node = list->head;
682
683 /* unlink the node */
684 list->head = node->next;
685 if (!list->head) {
686 /* empty list */
687 list->tail = NULL;
688 }
689
690 *msg = node->mesg;
691 *len = node->len;
692 if (conn)
693 *conn = node->conn;
694
695 list->numMessages--;
696
697 crFree(node);
698 retval = 1;
699 }
700 else {
701 *msg = NULL;
702 *len = 0;
703 retval = 0;
704 }
705
706#ifdef CHROMIUM_THREADSAFE
707 crUnlockMutex(&list->lock);
708#endif
709
710 return retval;
711}
712
713
714/**
715 * Remove message from tail of list. Block until non-empty if needed.
716 * \param list the message list
717 * \param msg returns start of message
718 * \param len returns message length, in bytes
719 * \param conn returns connection associated with message (may be NULL)
720 */
721void
722crDequeueMessage(CRMessageList *list, CRMessage **msg, unsigned int *len,
723 CRConnection **conn)
724{
725 CRMessageListNode *node;
726
727#ifdef CHROMIUM_THREADSAFE
728 crLockMutex(&list->lock);
729#endif
730
731#ifdef CHROMIUM_THREADSAFE
732 while (!list->head) {
733 crWaitCondition(&list->nonEmpty, &list->lock);
734 }
735#else
736 CRASSERT(list->head);
737#endif
738
739 node = list->head;
740
741 /* unlink the node */
742 list->head = node->next;
743 if (!list->head) {
744 /* empty list */
745 list->tail = NULL;
746 }
747
748 *msg = node->mesg;
749 CRASSERT((*msg)->header.type);
750 *len = node->len;
751 if (conn)
752 *conn = node->conn;
753
754 list->numMessages--;
755
756 crFree(node);
757
758#ifdef CHROMIUM_THREADSAFE
759 crUnlockMutex(&list->lock);
760#endif
761}
762
763
764
765/**
766 * Send a set of commands on a connection. Pretty straightforward, just
767 * error checking, byte counting, and a dispatch to the protocol's
768 * "send" implementation.
769 * The payload will be prefixed by a 4-byte length field.
770 *
771 * \param conn the network connection
772 * \param bufp if non-null the buffer was provided by the network layer
773 * and will be returned to the 'free' pool after it's sent.
774 * \param start points to first byte to send, which must point to a CRMessage
775 * object!
776 * \param len number of bytes to send
777 */
778void
779crNetSend(CRConnection *conn, void **bufp, const void *start, unsigned int len)
780{
781 CRMessage *msg = (CRMessage *) start;
782
783 CRASSERT( conn );
784 CRASSERT( len > 0 );
785 if ( bufp ) {
786 /* The region from [start .. start + len - 1] must lie inside the
787 * buffer pointed to by *bufp.
788 */
789 CRASSERT( start >= *bufp );
790 CRASSERT( (unsigned char *) start + len <=
791 (unsigned char *) *bufp + conn->buffer_size );
792 }
793
794#ifdef DEBUG
795 if ( conn->send_credits > CR_INITIAL_RECV_CREDITS )
796 {
797 crError( "crNetSend: send_credits=%u, looks like there is a leak (max=%u)",
798 conn->send_credits, CR_INITIAL_RECV_CREDITS );
799 }
800#endif
801
802 conn->total_bytes_sent += len;
803
804 msg->header.conn_id = conn->id;
805 conn->Send( conn, bufp, start, len );
806}
807
808
809/**
810 * Like crNetSend(), but the network layer is free to discard the data
811 * if something goes wrong. In particular, the UDP layer might discard
812 * the data in the event of transmission errors.
813 */
814void crNetBarf( CRConnection *conn, void **bufp,
815 const void *start, unsigned int len )
816{
817 CRMessage *msg = (CRMessage *) start;
818 CRASSERT( conn );
819 CRASSERT( len > 0 );
820 CRASSERT( conn->Barf );
821 if ( bufp ) {
822 CRASSERT( start >= *bufp );
823 CRASSERT( (unsigned char *) start + len <=
824 (unsigned char *) *bufp + conn->buffer_size );
825 }
826
827#ifdef DEBUG
828 if ( conn->send_credits > CR_INITIAL_RECV_CREDITS )
829 {
830 crError( "crNetBarf: send_credits=%u, looks like there is a "
831 "leak (max=%u)", conn->send_credits,
832 CR_INITIAL_RECV_CREDITS );
833 }
834#endif
835
836 conn->total_bytes_sent += len;
837
838 msg->header.conn_id = conn->id;
839 conn->Barf( conn, bufp, start, len );
840}
841
842
843/**
844 * Send a block of bytes across the connection without any sort of
845 * header/length information.
846 * \param conn the network connection
847 * \param buf points to first byte to send
848 * \param len number of bytes to send
849 */
850void crNetSendExact( CRConnection *conn, const void *buf, unsigned int len )
851{
852 CRASSERT(conn->SendExact);
853 conn->SendExact( conn, buf, len );
854}
855
856
857/**
858 * Connect to a server, as specified by the 'name' and 'buffer_size' fields
859 * of the CRNetServer parameter.
860 * When done, the CrNetServer's conn field will be initialized.
861 */
862void crNetServerConnect( CRNetServer *ns
863#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
864 , struct VBOXUHGSMI *pHgsmi
865#endif
866)
867{
868 ns->conn = crNetConnectToServer( ns->name, DEFAULT_SERVER_PORT,
869 ns->buffer_size, 0
870#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
871 , pHgsmi
872#endif
873 );
874}
875
876/**
877 * Actually set up the specified connection.
878 * Apparently, this is only called from the crNetConnectToServer function.
879 */
880int crNetConnect( CRConnection *conn )
881{
882 return conn->Connect( conn );
883}
884
885
886/**
887 * Tear down a network connection (close the socket, etc).
888 */
889void crNetDisconnect( CRConnection *conn )
890{
891 conn->Disconnect( conn );
892 crFree( conn->hostname );
893#ifdef CHROMIUM_THREADSAFE
894 crFreeMutex( &conn->messageList.lock );
895#endif
896 crFree( conn );
897}
898
899
900/**
901 * Actually set up the specified connection.
902 * Apparently, this is only called from the crNetConnectToServer function.
903 */
904void crNetAccept( CRConnection *conn, const char *hostname, unsigned short port )
905{
906 conn->Accept( conn, hostname, port );
907}
908
909
910/**
911 * Do a blocking receive on a particular connection. This only
912 * really works for TCPIP, but it's really only used (right now) by
913 * the mothership client library.
914 * Read exactly the number of bytes specified (no headers/prefixes).
915 */
916void crNetSingleRecv( CRConnection *conn, void *buf, unsigned int len )
917{
918 if (conn->type != CR_TCPIP)
919 {
920 crError( "Can't do a crNetSingleReceive on anything other than TCPIP." );
921 }
922 conn->Recv( conn, buf, len );
923}
924
925
926/**
927 * Receive a chunk of a CR_MESSAGE_MULTI_BODY/TAIL transmission.
928 * \param conn the network connection
929 * \param msg the incoming multi-part message
930 * \param len number of bytes in the message
931 */
932static void
933crNetRecvMulti( CRConnection *conn, CRMessageMulti *msg, unsigned int len )
934{
935 CRMultiBuffer *multi = &(conn->multi);
936 unsigned char *src, *dst;
937
938 CRASSERT( len > sizeof(*msg) );
939 len -= sizeof(*msg);
940
941 /* Check if there's enough room in the multi-buffer to append 'len' bytes */
942 if ( len + multi->len > multi->max )
943 {
944 if ( multi->max == 0 )
945 {
946 multi->len = conn->sizeof_buffer_header;
947 multi->max = 8192; /* arbitrary initial size */
948 }
949 /* grow the buffer by 2x until it's big enough */
950 while ( len + multi->len > multi->max )
951 {
952 multi->max <<= 1;
953 }
954 crRealloc( &multi->buf, multi->max );
955 }
956
957 dst = (unsigned char *) multi->buf + multi->len;
958 src = (unsigned char *) msg + sizeof(*msg);
959 crMemcpy( dst, src, len );
960 multi->len += len;
961
962 if (msg->header.type == CR_MESSAGE_MULTI_TAIL)
963 {
964 /* OK, we've collected the last chunk of the multi-part message */
965 conn->HandleNewMessage(
966 conn,
967 (CRMessage *) (((char *) multi->buf) + conn->sizeof_buffer_header),
968 multi->len - conn->sizeof_buffer_header );
969
970 /* clean this up before calling the user */
971 multi->buf = NULL;
972 multi->len = 0;
973 multi->max = 0;
974 }
975
976 /* Don't do this too early! */
977 conn->InstantReclaim( conn, (CRMessage *) msg );
978}
979
980
981/**
982 * Increment the connection's send_credits by msg->credits.
983 */
984static void
985crNetRecvFlowControl( CRConnection *conn, CRMessageFlowControl *msg,
986 unsigned int len )
987{
988 CRASSERT( len == sizeof(CRMessageFlowControl) );
989 conn->send_credits += (conn->swap ? SWAP32(msg->credits) : msg->credits);
990 conn->InstantReclaim( conn, (CRMessage *) msg );
991}
992
993#ifdef IN_GUEST
994/**
995 * Called by the main receive function when we get a CR_MESSAGE_WRITEBACK
996 * message. Writeback is used to implement glGet*() functions.
997 */
998static void
999crNetRecvWriteback( CRMessageWriteback *wb )
1000{
1001 int *writeback;
1002 crMemcpy( &writeback, &(wb->writeback_ptr), sizeof( writeback ) );
1003 (*writeback)--;
1004}
1005
1006
1007/**
1008 * Called by the main receive function when we get a CR_MESSAGE_READBACK
1009 * message. Used to implement glGet*() functions.
1010 */
1011static void
1012crNetRecvReadback( CRMessageReadback *rb, unsigned int len )
1013{
1014 /* minus the header, the destination pointer,
1015 * *and* the implicit writeback pointer at the head. */
1016
1017 int payload_len = len - sizeof( *rb );
1018 int *writeback;
1019 void *dest_ptr;
1020 crMemcpy( &writeback, &(rb->writeback_ptr), sizeof( writeback ) );
1021 crMemcpy( &dest_ptr, &(rb->readback_ptr), sizeof( dest_ptr ) );
1022
1023 (*writeback)--;
1024 crMemcpy( dest_ptr, ((char *)rb) + sizeof(*rb), payload_len );
1025}
1026#endif
1027
1028/**
1029 * This is used by the SPUs that do packing (such as Pack, Tilesort and
1030 * Replicate) to process ReadPixels messages. We can't call this directly
1031 * from the message loop below because the SPU's have other housekeeping
1032 * to do for ReadPixels (such as decrementing counters).
1033 */
1034void
1035crNetRecvReadPixels( const CRMessageReadPixels *rp, unsigned int len )
1036{
1037 int payload_len = len - sizeof( *rp );
1038 char *dest_ptr;
1039 const char *src_ptr = (const char *) rp + sizeof(*rp);
1040
1041 /* set dest_ptr value */
1042 crMemcpy( &(dest_ptr), &(rp->pixels), sizeof(dest_ptr));
1043
1044 /* store pixel data in app's memory */
1045 if (rp->alignment == 1 &&
1046 rp->skipRows == 0 &&
1047 rp->skipPixels == 0 &&
1048 (rp->rowLength == 0 || rp->rowLength == rp->width)) {
1049 /* no special packing is needed */
1050 crMemcpy( dest_ptr, src_ptr, payload_len );
1051 }
1052 else {
1053 /* need special packing */
1054 CRPixelPackState packing;
1055 packing.skipRows = rp->skipRows;
1056 packing.skipPixels = rp->skipPixels;
1057 packing.alignment = rp->alignment;
1058 packing.rowLength = rp->rowLength;
1059 packing.imageHeight = 0;
1060 packing.skipImages = 0;
1061 packing.swapBytes = GL_FALSE;
1062 packing.psLSBFirst = GL_FALSE;
1063 crPixelCopy2D( rp->width, rp->height,
1064 dest_ptr, rp->format, rp->type, &packing,
1065 src_ptr, rp->format, rp->type, /*unpacking*/NULL);
1066 }
1067}
1068
1069
1070
1071/**
1072 * If an incoming message is not consumed by any of the connection's
1073 * receive callbacks, this function will get called.
1074 *
1075 * XXX Make this function static???
1076 */
1077void
1078crNetDefaultRecv( CRConnection *conn, CRMessage *msg, unsigned int len )
1079{
1080 CRMessage *pRealMsg;
1081
1082 pRealMsg = (msg->header.type!=CR_MESSAGE_REDIR_PTR) ? msg : (CRMessage*) msg->redirptr.pMessage;
1083
1084 switch (pRealMsg->header.type)
1085 {
1086 case CR_MESSAGE_GATHER:
1087 break;
1088 case CR_MESSAGE_MULTI_BODY:
1089 case CR_MESSAGE_MULTI_TAIL:
1090 crNetRecvMulti( conn, &(pRealMsg->multi), len );
1091 return;
1092 case CR_MESSAGE_FLOW_CONTROL:
1093 crNetRecvFlowControl( conn, &(pRealMsg->flowControl), len );
1094 return;
1095 case CR_MESSAGE_OPCODES:
1096 case CR_MESSAGE_OOB:
1097 {
1098 /*CRMessageOpcodes *ops = (CRMessageOpcodes *) msg;
1099 *unsigned char *data_ptr = (unsigned char *) ops + sizeof( *ops) + ((ops->numOpcodes + 3 ) & ~0x03);
1100 *crDebugOpcodes( stdout, data_ptr-1, ops->numOpcodes ); */
1101 }
1102 break;
1103 case CR_MESSAGE_READ_PIXELS:
1104 WARN(( "Can't handle read pixels" ));
1105 return;
1106 case CR_MESSAGE_WRITEBACK:
1107#ifdef IN_GUEST
1108 crNetRecvWriteback( &(pRealMsg->writeback) );
1109#else
1110 WARN(("CR_MESSAGE_WRITEBACK not expected\n"));
1111#endif
1112 return;
1113 case CR_MESSAGE_READBACK:
1114#ifdef IN_GUEST
1115 crNetRecvReadback( &(pRealMsg->readback), len );
1116#else
1117 WARN(("CR_MESSAGE_READBACK not expected\n"));
1118#endif
1119 return;
1120 case CR_MESSAGE_CRUT:
1121 /* nothing */
1122 break;
1123 default:
1124 /* We can end up here if anything strange happens in
1125 * the GM layer. In particular, if the user tries to
1126 * send unpinned memory over GM it gets sent as all
1127 * 0xAA instead. This can happen when a program exits
1128 * ungracefully, so the GM is still DMAing memory as
1129 * it is disappearing out from under it. We can also
1130 * end up here if somebody adds a message type, and
1131 * doesn't put it in the above case block. That has
1132 * an obvious fix. */
1133 {
1134 char string[128];
1135 crBytesToString( string, sizeof(string), msg, len );
1136 WARN(("crNetDefaultRecv: received a bad message: type=%d buf=[%s]\n"
1137 "Did you add a new message type and forget to tell "
1138 "crNetDefaultRecv() about it?\n",
1139 msg->header.type, string ));
1140 }
1141 }
1142
1143 /* If we make it this far, it's not a special message, so append it to
1144 * the end of the connection's list of received messages.
1145 */
1146 crEnqueueMessage(&conn->messageList, msg, len, conn);
1147}
1148
1149
1150/**
1151 * Default handler for receiving data. Called via crNetRecv().
1152 * Typically, the various implementations of the network layer call this.
1153 * \param msg this is the address of the message (of <len> bytes) the
1154 * first part of which is a CRMessage union.
1155 */
1156void
1157crNetDispatchMessage( CRNetReceiveFuncList *rfl, CRConnection *conn,
1158 CRMessage *msg, unsigned int len )
1159{
1160 for ( ; rfl ; rfl = rfl->next)
1161 {
1162 if (rfl->recv( conn, msg, len ))
1163 {
1164 /* Message was consumed by somebody (maybe a SPU).
1165 * All done.
1166 */
1167 return;
1168 }
1169 }
1170 /* Append the message to the connection's message list. It'll be
1171 * consumed later (by crNetPeekMessage or crNetGetMessage and
1172 * then freed with a call to crNetFree()). At this point, the buffer
1173 * *must* have been allocated with crNetAlloc!
1174 */
1175 crNetDefaultRecv( conn, msg, len );
1176}
1177
1178
1179/**
1180 * Return number of messages queued up on the given connection.
1181 */
1182int
1183crNetNumMessages(CRConnection *conn)
1184{
1185 return conn->messageList.numMessages;
1186}
1187
1188
1189/**
1190 * Get the next message in the connection's message list. These are
1191 * message that have already been received. We do not try to read more
1192 * bytes from the network connection.
1193 *
1194 * The crNetFree() function should be called when finished with the message!
1195 *
1196 * \param conn the network connection
1197 * \param message returns a pointer to the next message
1198 * \return length of message (header + payload, in bytes)
1199 */
1200unsigned int
1201crNetPeekMessage( CRConnection *conn, CRMessage **message )
1202{
1203 unsigned int len;
1204 CRConnection *dummyConn = NULL;
1205 if (crDequeueMessageNoBlock(&conn->messageList, message, &len, &dummyConn))
1206 return len;
1207 else
1208 return 0;
1209}
1210
1211
1212/**
1213 * Get the next message from the given network connection. If there isn't
1214 * one already in the linked list of received messages, call crNetRecv()
1215 * until we get something.
1216 *
1217 * \param message returns pointer to the message
1218 * \return total length of message (header + payload, in bytes)
1219 */
1220unsigned int
1221crNetGetMessage( CRConnection *conn, CRMessage **message )
1222{
1223 /* Keep getting work to do */
1224 for (;;)
1225 {
1226 int len = crNetPeekMessage( conn, message );
1227 if (len)
1228 return len;
1229 crNetRecv(
1230#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
1231 conn
1232#endif
1233 );
1234 }
1235
1236#if !defined(WINDOWS) && !defined(IRIX) && !defined(IRIX64)
1237 /* silence compiler */
1238 return 0;
1239#endif
1240}
1241
1242
1243/**
1244 * Read a \n-terminated string from a connection. Replace the \n with \0.
1245 * Useful for reading from the mothership.
1246 * \note This is an extremely inefficient way to read a string!
1247 *
1248 * \param conn the network connection
1249 * \param buf buffer in which to place results
1250 */
1251void crNetReadline( CRConnection *conn, void *buf )
1252{
1253 char *temp, c;
1254
1255 if (!conn || conn->type == CR_NO_CONNECTION)
1256 return;
1257
1258 if (conn->type != CR_TCPIP)
1259 {
1260 crError( "Can't do a crNetReadline on anything other than TCPIP (%d).",conn->type );
1261 }
1262 temp = (char*)buf;
1263 for (;;)
1264 {
1265 conn->Recv( conn, &c, 1 );
1266 if (c == '\n')
1267 {
1268 *temp = '\0';
1269 return;
1270 }
1271 *(temp++) = c;
1272 }
1273}
1274
1275#ifdef IN_GUEST
1276uint32_t crNetHostCapsGet(void)
1277{
1278#ifdef VBOX_WITH_HGCM
1279 if ( cr_net.use_hgcm )
1280 return crVBoxHGCMHostCapsGet();
1281#endif
1282 WARN(("HostCaps supportted for HGCM only!"));
1283 return 0;
1284}
1285#endif
1286
1287/**
1288 * The big boy -- call this function to see (non-blocking) if there is
1289 * any pending work. If there is, the networking layer's "work received"
1290 * handler will be called, so this function only returns a flag. Work
1291 * is assumed to be placed on queues for processing by the handler.
1292 */
1293int crNetRecv(
1294#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
1295 CRConnection *conn
1296#else
1297 void
1298#endif
1299 )
1300{
1301 int found_work = 0;
1302
1303 if ( cr_net.use_tcpip )
1304 found_work += crTCPIPRecv();
1305#ifdef VBOX_WITH_HGCM
1306 if ( cr_net.use_hgcm )
1307 found_work += crVBoxHGCMRecv(
1308#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST)
1309 conn
1310#endif
1311 );
1312#endif
1313#ifdef SDP_SUPPORT
1314 if ( cr_net.use_sdp )
1315 found_work += crSDPRecv();
1316#endif
1317#ifdef IB_SUPPORT
1318 if ( cr_net.use_ib )
1319 found_work += crIBRecv();
1320#endif
1321 if ( cr_net.use_udp )
1322 found_work += crUDPTCPIPRecv();
1323
1324 if ( cr_net.use_file )
1325 found_work += crFileRecv();
1326
1327#ifdef GM_SUPPORT
1328 if ( cr_net.use_gm )
1329 found_work += crGmRecv();
1330#endif
1331
1332#ifdef TEAC_SUPPORT
1333 if ( cr_net.use_teac )
1334 found_work += crTeacRecv();
1335#endif
1336
1337#ifdef TCSCOMM_SUPPORT
1338 if ( cr_net.use_tcscomm )
1339 found_work += crTcscommRecv();
1340#endif
1341
1342 return found_work;
1343}
1344
1345
1346/**
1347 * Teac/TSComm only
1348 */
1349void
1350crNetSetRank( int my_rank )
1351{
1352 cr_net.my_rank = my_rank;
1353#ifdef TEAC_SUPPORT
1354 crTeacSetRank( cr_net.my_rank );
1355#endif
1356#ifdef TCSCOMM_SUPPORT
1357 crTcscommSetRank( cr_net.my_rank );
1358#endif
1359}
1360
1361/**
1362 * Teac/TSComm only
1363 */
1364void
1365crNetSetContextRange( int low_context, int high_context )
1366{
1367#if !defined(TEAC_SUPPORT) && !defined(TCSCOMM_SUPPORT)
1368 (void)low_context; (void)high_context;
1369#endif
1370#ifdef TEAC_SUPPORT
1371 crTeacSetContextRange( low_context, high_context );
1372#endif
1373#ifdef TCSCOMM_SUPPORT
1374 crTcscommSetContextRange( low_context, high_context );
1375#endif
1376}
1377
1378/**
1379 * Teac/TSComm only
1380 */
1381void
1382crNetSetNodeRange( const char *low_node, const char *high_node )
1383{
1384#if !defined(TEAC_SUPPORT) && !defined(TCSCOMM_SUPPORT)
1385 (void)low_node; (void)high_node;
1386#endif
1387#ifdef TEAC_SUPPORT
1388 crTeacSetNodeRange( low_node, high_node );
1389#endif
1390#ifdef TCSCOMM_SUPPORT
1391 crTcscommSetNodeRange( low_node, high_node );
1392#endif
1393}
1394
1395/**
1396 * Teac/TSComm only
1397 */
1398void
1399crNetSetKey( const unsigned char* key, const int keyLength )
1400{
1401#ifdef TEAC_SUPPORT
1402 crTeacSetKey( key, keyLength );
1403#else
1404 (void)key; (void)keyLength;
1405#endif
1406}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette