Annotation of embedaddon/ntp/ports/winnt/ntpd/ntp_iocompletionport.c, revision 1.1
1.1 ! misho 1: #ifdef HAVE_CONFIG_H
! 2: # include <config.h>
! 3: #endif
! 4:
! 5: #if defined (HAVE_IO_COMPLETION_PORT)
! 6:
! 7: #include <stddef.h>
! 8: #include <stdio.h>
! 9: #include <process.h>
! 10: #include <syslog.h>
! 11:
! 12: #include "ntp_stdlib.h"
! 13: #include "ntp_machine.h"
! 14: #include "ntp_fp.h"
! 15: #include "ntp.h"
! 16: #include "ntpd.h"
! 17: #include "ntp_refclock.h"
! 18: #include "ntp_iocompletionport.h"
! 19: #include "transmitbuff.h"
! 20: #include "ntp_request.h"
! 21: #include "ntp_assert.h"
! 22: #include "clockstuff.h"
! 23: #include "ntp_io.h"
! 24: #include "ntp_lists.h"
! 25: #include "clockstuff.h"
! 26:
! 27: /*
! 28: * Request types
! 29: */
! 30: enum {
! 31: SOCK_RECV,
! 32: SOCK_SEND,
! 33: SERIAL_WAIT,
! 34: SERIAL_READ,
! 35: SERIAL_WRITE
! 36: };
! 37:
! 38: #ifdef _MSC_VER
! 39: # pragma warning(push)
! 40: # pragma warning(disable: 201) /* nonstd extension nameless union */
! 41: #endif
! 42:
! 43: typedef struct IoCompletionInfo {
! 44: OVERLAPPED overlapped; /* must be first */
! 45: int request_type;
! 46: union {
! 47: recvbuf_t * recv_buf;
! 48: transmitbuf_t * trans_buf;
! 49: };
! 50: #ifdef DEBUG
! 51: struct IoCompletionInfo *link;
! 52: #endif
! 53: } IoCompletionInfo;
! 54:
! 55: #ifdef _MSC_VER
! 56: # pragma warning(pop)
! 57: #endif
! 58:
! 59: /*
! 60: * local function definitions
! 61: */
! 62: static int QueueSerialWait(struct refclockio *, recvbuf_t *buff, IoCompletionInfo *lpo, BOOL clear_timestamp);
! 63:
! 64: static int OnSocketRecv(ULONG_PTR, IoCompletionInfo *, DWORD, int);
! 65: static int OnSerialWaitComplete(ULONG_PTR, IoCompletionInfo *, DWORD, int);
! 66: static int OnSerialReadComplete(ULONG_PTR, IoCompletionInfo *, DWORD, int);
! 67: static int OnWriteComplete(ULONG_PTR, IoCompletionInfo *, DWORD, int);
! 68:
! 69: /* keep a list to traverse to free memory on debug builds */
! 70: #ifdef DEBUG
! 71: static void free_io_completion_port_mem(void);
! 72: IoCompletionInfo * compl_info_list;
! 73: CRITICAL_SECTION compl_info_lock;
! 74: #define LOCK_COMPL() EnterCriticalSection(&compl_info_lock);
! 75: #define UNLOCK_COMPL() LeaveCriticalSection(&compl_info_lock);
! 76: #endif
! 77:
! 78: /* #define USE_HEAP */
! 79:
! 80: #ifdef USE_HEAP
! 81: static HANDLE hHeapHandle = NULL;
! 82: #endif
! 83:
! 84: static HANDLE hIoCompletionPort = NULL;
! 85:
! 86: static HANDLE WaitableIoEventHandle = NULL;
! 87: static HANDLE WaitableExitEventHandle = NULL;
! 88:
! 89: #ifdef NTPNEEDNAMEDHANDLE
! 90: #define WAITABLEIOEVENTHANDLE "WaitableIoEventHandle"
! 91: #else
! 92: #define WAITABLEIOEVENTHANDLE NULL
! 93: #endif
! 94:
! 95: #define MAXHANDLES 3
! 96: HANDLE WaitHandles[MAXHANDLES] = { NULL, NULL, NULL };
! 97:
! 98: IoCompletionInfo *
! 99: GetHeapAlloc(char *fromfunc)
! 100: {
! 101: IoCompletionInfo *lpo;
! 102:
! 103: #ifdef USE_HEAP
! 104: lpo = HeapAlloc(hHeapHandle,
! 105: HEAP_ZERO_MEMORY,
! 106: sizeof(IoCompletionInfo));
! 107: #else
! 108: lpo = emalloc(sizeof(*lpo));
! 109: memset(lpo, 0, sizeof(*lpo));
! 110: #endif
! 111: DPRINTF(3, ("Allocation %d memory for %s, ptr %x\n", sizeof(IoCompletionInfo), fromfunc, lpo));
! 112:
! 113: #ifdef DEBUG
! 114: LOCK_COMPL();
! 115: LINK_SLIST(compl_info_list, lpo, link);
! 116: UNLOCK_COMPL();
! 117: #endif
! 118:
! 119: return (lpo);
! 120: }
! 121:
! 122: void
! 123: FreeHeap(IoCompletionInfo *lpo, char *fromfunc)
! 124: {
! 125: #ifdef DEBUG
! 126: IoCompletionInfo *unlinked;
! 127:
! 128: DPRINTF(3, ("Freeing memory for %s, ptr %x\n", fromfunc, lpo));
! 129:
! 130: LOCK_COMPL();
! 131: UNLINK_SLIST(unlinked, compl_info_list, lpo, link,
! 132: IoCompletionInfo);
! 133: UNLOCK_COMPL();
! 134: #endif
! 135:
! 136: #ifdef USE_HEAP
! 137: HeapFree(hHeapHandle, 0, lpo);
! 138: #else
! 139: free(lpo);
! 140: #endif
! 141: }
! 142:
! 143: transmitbuf_t *
! 144: get_trans_buf()
! 145: {
! 146: transmitbuf_t *tb = emalloc(sizeof(*tb));
! 147: return (tb);
! 148: }
! 149:
! 150: void
! 151: free_trans_buf(transmitbuf_t *tb)
! 152: {
! 153: free(tb);
! 154: }
! 155:
! 156: HANDLE
! 157: get_io_event()
! 158: {
! 159: return( WaitableIoEventHandle );
! 160: }
! 161: HANDLE
! 162: get_exit_event()
! 163: {
! 164: return( WaitableExitEventHandle );
! 165: }
! 166:
! 167: /* This function will add an entry to the I/O completion port
! 168: * that will signal the I/O thread to exit (gracefully)
! 169: */
! 170: static void
! 171: signal_io_completion_port_exit()
! 172: {
! 173: if (!PostQueuedCompletionStatus(hIoCompletionPort, 0, 0, 0)) {
! 174: msyslog(LOG_ERR, "Can't request service thread to exit: %m");
! 175: exit(1);
! 176: }
! 177: }
! 178:
! 179: static unsigned WINAPI
! 180: iocompletionthread(void *NotUsed)
! 181: {
! 182: BOOL bSuccess = FALSE;
! 183: int errstatus = 0;
! 184: DWORD BytesTransferred = 0;
! 185: ULONG_PTR Key = 0;
! 186: IoCompletionInfo * lpo = NULL;
! 187: u_long time_next_ifscan_after_error = 0;
! 188:
! 189: UNUSED_ARG(NotUsed);
! 190:
! 191: /*
! 192: * socket and refclock receive call gettimeofday()
! 193: * so the I/O thread needs to be on the same
! 194: * processor as the main and timing threads
! 195: * to ensure consistent QueryPerformanceCounter()
! 196: * results.
! 197: */
! 198: lock_thread_to_processor(GetCurrentThread());
! 199:
! 200: /* Set the thread priority high enough so I/O will
! 201: * preempt normal recv packet processing, but not
! 202: * higher than the timer sync thread.
! 203: */
! 204: if (!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL)) {
! 205: msyslog(LOG_ERR, "Can't set thread priority: %m");
! 206: }
! 207:
! 208: while (TRUE) {
! 209: bSuccess = GetQueuedCompletionStatus(
! 210: hIoCompletionPort,
! 211: &BytesTransferred,
! 212: &Key,
! 213: (LPOVERLAPPED *) &lpo,
! 214: INFINITE);
! 215: if (lpo == NULL)
! 216: {
! 217: DPRINTF(2, ("Overlapped IO Thread Exiting\n"));
! 218: break; /* fail */
! 219: }
! 220:
! 221: /*
! 222: * Deal with errors
! 223: */
! 224: if (bSuccess)
! 225: errstatus = 0;
! 226: else
! 227: {
! 228: errstatus = GetLastError();
! 229: if (BytesTransferred == 0)
! 230: {
! 231: if (WSA_OPERATION_ABORTED == errstatus) {
! 232: DPRINTF(4, ("Transfer Operation aborted\n"));
! 233: } else if (ERROR_UNEXP_NET_ERR == errstatus) {
! 234: /*
! 235: * We get this error when trying to send an the network
! 236: * interface is gone or has lost link. Rescan interfaces
! 237: * to catch on sooner, but no more than once per minute.
! 238: * Once ntp is able to detect changes without polling
! 239: * this should be unneccessary
! 240: */
! 241: if (time_next_ifscan_after_error < current_time) {
! 242: time_next_ifscan_after_error = current_time + 60;
! 243: timer_interfacetimeout(current_time);
! 244: }
! 245: DPRINTF(4, ("sendto unexpected network error, interface may be down\n"));
! 246: }
! 247: }
! 248: else
! 249: {
! 250: msyslog(LOG_ERR, "sendto error after %d bytes: %m", BytesTransferred);
! 251: }
! 252: }
! 253:
! 254: /*
! 255: * Invoke the appropriate function based on
! 256: * the value of the request_type
! 257: */
! 258: switch(lpo->request_type)
! 259: {
! 260: case SERIAL_WAIT:
! 261: OnSerialWaitComplete(Key, lpo, BytesTransferred, errstatus);
! 262: break;
! 263: case SERIAL_READ:
! 264: OnSerialReadComplete(Key, lpo, BytesTransferred, errstatus);
! 265: break;
! 266: case SOCK_RECV:
! 267: OnSocketRecv(Key, lpo, BytesTransferred, errstatus);
! 268: break;
! 269: case SOCK_SEND:
! 270: case SERIAL_WRITE:
! 271: OnWriteComplete(Key, lpo, BytesTransferred, errstatus);
! 272: break;
! 273: default:
! 274: DPRINTF(1, ("Unknown request type %d found in completion port\n",
! 275: lpo->request_type));
! 276: break;
! 277: }
! 278: }
! 279:
! 280: return 0;
! 281: }
! 282:
! 283: /* Create/initialise the I/O creation port
! 284: */
! 285: void
! 286: init_io_completion_port(
! 287: void
! 288: )
! 289: {
! 290: unsigned tid;
! 291: HANDLE thread;
! 292:
! 293: #ifdef DEBUG
! 294: InitializeCriticalSection(&compl_info_lock);
! 295: atexit(&free_io_completion_port_mem);
! 296: #endif
! 297:
! 298: #ifdef USE_HEAP
! 299: /*
! 300: * Create a handle to the Heap
! 301: */
! 302: hHeapHandle = HeapCreate(0, 20*sizeof(IoCompletionInfo), 0);
! 303: if (hHeapHandle == NULL)
! 304: {
! 305: msyslog(LOG_ERR, "Can't initialize Heap: %m");
! 306: exit(1);
! 307: }
! 308: #endif
! 309:
! 310: #if 0 /* transmitbuff.c unused, no need to initialize it */
! 311: init_transmitbuff();
! 312: #endif
! 313:
! 314: /* Create the event used to signal an IO event
! 315: */
! 316: WaitableIoEventHandle = CreateEvent(NULL, FALSE, FALSE, WAITABLEIOEVENTHANDLE);
! 317: if (WaitableIoEventHandle == NULL) {
! 318: msyslog(LOG_ERR,
! 319: "Can't create I/O event handle: %m - another process may be running - EXITING");
! 320: exit(1);
! 321: }
! 322: /* Create the event used to signal an exit event
! 323: */
! 324: WaitableExitEventHandle = CreateEvent(NULL, FALSE, FALSE, NULL);
! 325: if (WaitableExitEventHandle == NULL) {
! 326: msyslog(LOG_ERR,
! 327: "Can't create exit event handle: %m - EXITING");
! 328: exit(1);
! 329: }
! 330:
! 331: /* Create the IO completion port
! 332: */
! 333: hIoCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
! 334: if (hIoCompletionPort == NULL) {
! 335: msyslog(LOG_ERR, "Can't create I/O completion port: %m");
! 336: exit(1);
! 337: }
! 338:
! 339: /*
! 340: * Initialize the Wait Handles
! 341: */
! 342: WaitHandles[0] = WaitableIoEventHandle;
! 343: WaitHandles[1] = WaitableExitEventHandle; /* exit request */
! 344: WaitHandles[2] = get_timer_handle();
! 345:
! 346: /* Have one thread servicing I/O - there were 4, but this would
! 347: * somehow cause NTP to stop replying to ntpq requests; TODO
! 348: */
! 349: thread = (HANDLE)_beginthreadex(
! 350: NULL,
! 351: 0,
! 352: iocompletionthread,
! 353: NULL,
! 354: CREATE_SUSPENDED,
! 355: &tid);
! 356: ResumeThread(thread);
! 357: CloseHandle(thread);
! 358: }
! 359:
! 360:
! 361: #ifdef DEBUG
! 362: static void
! 363: free_io_completion_port_mem(
! 364: void
! 365: )
! 366: {
! 367: IoCompletionInfo * pci;
! 368:
! 369: #if defined(_MSC_VER) && defined (_DEBUG)
! 370: _CrtCheckMemory();
! 371: #endif
! 372: LOCK_COMPL();
! 373: while ((pci = compl_info_list) != NULL) {
! 374:
! 375: #if 0 /* sockaddr with received-from address in recvbuf */
! 376: /* is sometimes modified by system after we free it */
! 377: /* triggering heap corruption warning -- find a */
! 378: /* better way to free it after I/O is surely done */
! 379: /* this handles both xmit and recv buffs */
! 380: if (pci->recv_buf != NULL) {
! 381: DPRINTF(1, ("freeing xmit/recv buff %p\n", pci->recv_buf));
! 382: free(pci->recv_buf);
! 383: }
! 384: #endif
! 385:
! 386: FreeHeap(pci, "free_io_completion_port_mem");
! 387: /* FreeHeap() removed this item from compl_info_list */
! 388: }
! 389: UNLOCK_COMPL()
! 390:
! 391: #if defined(_MSC_VER) && defined (_DEBUG)
! 392: _CrtCheckMemory();
! 393: #endif
! 394: }
! 395: #endif /* DEBUG */
! 396:
! 397:
! 398: void
! 399: uninit_io_completion_port(
! 400: void
! 401: )
! 402: {
! 403: if (hIoCompletionPort != NULL) {
! 404: /* Get each of the service threads to exit
! 405: */
! 406: signal_io_completion_port_exit();
! 407: }
! 408: }
! 409:
! 410:
! 411: static int
! 412: QueueSerialWait(
! 413: struct refclockio * rio,
! 414: recvbuf_t * buff,
! 415: IoCompletionInfo * lpo,
! 416: BOOL clear_timestamp
! 417: )
! 418: {
! 419: lpo->request_type = SERIAL_WAIT;
! 420: lpo->recv_buf = buff;
! 421:
! 422: if (clear_timestamp)
! 423: memset(&buff->recv_time, 0, sizeof(buff->recv_time));
! 424:
! 425: buff->fd = _get_osfhandle(rio->fd);
! 426: if (!WaitCommEvent((HANDLE) buff->fd, (DWORD *)&buff->recv_buffer, (LPOVERLAPPED) lpo)) {
! 427: if (ERROR_IO_PENDING != GetLastError()) {
! 428: msyslog(LOG_ERR, "Can't wait on Refclock: %m");
! 429: freerecvbuf(buff);
! 430: return 0;
! 431: }
! 432: }
! 433: return 1;
! 434: }
! 435:
! 436:
! 437: static int
! 438: OnSerialWaitComplete(ULONG_PTR i, IoCompletionInfo *lpo, DWORD Bytes, int errstatus)
! 439: {
! 440: recvbuf_t *buff;
! 441: struct refclockio * rio = (struct refclockio *) i;
! 442: struct peer *pp;
! 443: l_fp arrival_time;
! 444: DWORD comm_mask;
! 445: DWORD modem_status;
! 446: static const l_fp zero_time = { 0 };
! 447: BOOL rc;
! 448:
! 449: get_systime(&arrival_time);
! 450:
! 451: /*
! 452: * Get the recvbuf pointer from the overlapped buffer.
! 453: */
! 454: buff = lpo->recv_buf;
! 455: comm_mask = (*(DWORD *)&buff->recv_buffer);
! 456: #ifdef DEBUG
! 457: if (errstatus || comm_mask & ~(EV_RXFLAG | EV_RLSD)) {
! 458: msyslog(LOG_ERR, "WaitCommEvent returned unexpected mask %x errstatus %d",
! 459: comm_mask, errstatus);
! 460: exit(-1);
! 461: }
! 462: #endif
! 463: if (comm_mask & EV_RLSD) {
! 464: modem_status = 0;
! 465: GetCommModemStatus((HANDLE)buff->fd, &modem_status);
! 466: if (modem_status & MS_RLSD_ON) {
! 467: /*
! 468: * Use the timestamp from this PPS CD not
! 469: * the later end of line.
! 470: */
! 471: buff->recv_time = arrival_time;
! 472: }
! 473:
! 474: if (!(comm_mask & EV_RXFLAG)) {
! 475: /*
! 476: * if we didn't see an end of line yet
! 477: * issue another wait for it.
! 478: */
! 479: QueueSerialWait(rio, buff, lpo, FALSE);
! 480: return 1;
! 481: }
! 482: }
! 483:
! 484: /*
! 485: * We've detected the end of line of serial input.
! 486: * Use this timestamp unless we already have a CD PPS
! 487: * timestamp in buff->recv_time.
! 488: */
! 489: if (memcmp(&buff->recv_time, &zero_time, sizeof buff->recv_time)) {
! 490: /*
! 491: * We will first see a user PPS timestamp here on either
! 492: * the first or second line of text. Log a one-time
! 493: * message while processing the second line.
! 494: */
! 495: if (1 == rio->recvcount) {
! 496: pp = (struct peer *)rio->srcclock;
! 497: msyslog(LOG_NOTICE, "Using user-mode PPS timestamp for %s",
! 498: refnumtoa(&pp->srcadr));
! 499: }
! 500: } else {
! 501: buff->recv_time = arrival_time;
! 502: }
! 503:
! 504: /*
! 505: * Now that we have a complete line waiting, read it.
! 506: * There is still a race here, but we're likely to win.
! 507: */
! 508:
! 509: lpo->request_type = SERIAL_READ;
! 510:
! 511: rc = ReadFile(
! 512: (HANDLE)buff->fd,
! 513: buff->recv_buffer,
! 514: sizeof(buff->recv_buffer),
! 515: NULL,
! 516: (LPOVERLAPPED)lpo);
! 517:
! 518: if (!rc && ERROR_IO_PENDING != GetLastError()) {
! 519: msyslog(LOG_ERR, "Can't read from Refclock: %m");
! 520: freerecvbuf(buff);
! 521: return 0;
! 522: }
! 523:
! 524: return 1;
! 525: }
! 526:
! 527: /* Return 1 on Successful Read */
! 528: static int
! 529: OnSerialReadComplete(ULONG_PTR i, IoCompletionInfo *lpo, DWORD Bytes, int errstatus)
! 530: {
! 531: recvbuf_t * buff;
! 532: l_fp cr_time;
! 533: struct refclockio * rio;
! 534:
! 535: rio = (struct refclockio *)i;
! 536: /*
! 537: * Get the recvbuf pointer from the overlapped buffer.
! 538: */
! 539: buff = lpo->recv_buf;
! 540:
! 541: /*
! 542: * ignore 0 bytes read due to timeout's and closure on fd
! 543: */
! 544: if (!errstatus && Bytes) {
! 545: buff->recv_length = (int) Bytes;
! 546: buff->receiver = rio->clock_recv;
! 547: buff->dstadr = NULL;
! 548: buff->recv_srcclock = rio->srcclock;
! 549: packets_received++;
! 550: /*
! 551: * Eat the first line of input as it's possibly
! 552: * partial and if a PPS is present, it may not
! 553: * have fired since the port was opened.
! 554: */
! 555: if (rio->recvcount++) {
! 556: cr_time = buff->recv_time;
! 557: add_full_recv_buffer(buff);
! 558: /*
! 559: * Mimic Unix line discipline and assume CR/LF
! 560: * line termination. On Unix the CR terminates
! 561: * the line containing the timecode, and
! 562: * immediately after the LF terminates an empty
! 563: * line. So synthesize the empty LF-terminated
! 564: * line using the same CR timestamp. Both CR
! 565: * and LF are stripped by refclock_gtlin().
! 566: */
! 567: buff = get_free_recv_buffer_alloc();
! 568: buff->recv_time = cr_time;
! 569: buff->recv_length = 0;
! 570: buff->fd = _get_osfhandle(rio->fd);
! 571: buff->receiver = rio->clock_recv;
! 572: buff->dstadr = NULL;
! 573: buff->recv_srcclock = rio->srcclock;
! 574: add_full_recv_buffer(buff);
! 575: /*
! 576: * Now signal we have something to process
! 577: */
! 578: SetEvent(WaitableIoEventHandle);
! 579: buff = get_free_recv_buffer_alloc();
! 580: }
! 581: }
! 582:
! 583: QueueSerialWait(rio, buff, lpo, TRUE);
! 584:
! 585: return 1;
! 586: }
! 587:
! 588: /* Add a reference clock data structures I/O handles to
! 589: * the I/O completion port. Return 1 if any error.
! 590: */
! 591: int
! 592: io_completion_port_add_clock_io(
! 593: struct refclockio *rio
! 594: )
! 595: {
! 596: IoCompletionInfo *lpo;
! 597: recvbuf_t *buff;
! 598:
! 599: if (NULL == CreateIoCompletionPort(
! 600: (HANDLE)_get_osfhandle(rio->fd),
! 601: hIoCompletionPort,
! 602: (ULONG_PTR)rio,
! 603: 0)) {
! 604: msyslog(LOG_ERR, "Can't add COM port to i/o completion port: %m");
! 605: return 1;
! 606: }
! 607:
! 608: lpo = GetHeapAlloc("io_completion_port_add_clock_io");
! 609: if (NULL == lpo) {
! 610: msyslog(LOG_ERR, "Can't allocate heap for completion port: %m");
! 611: return 1;
! 612: }
! 613:
! 614: buff = get_free_recv_buffer_alloc();
! 615: QueueSerialWait(rio, buff, lpo, TRUE);
! 616: return 0;
! 617: }
! 618:
! 619: /*
! 620: * Queue a receiver on a socket. Returns 0 if no buffer can be queued
! 621: *
! 622: * Note: As per the winsock documentation, we use WSARecvFrom. Using
! 623: * ReadFile() is less efficient.
! 624: */
! 625: static unsigned long
! 626: QueueSocketRecv(
! 627: SOCKET s,
! 628: recvbuf_t *buff,
! 629: IoCompletionInfo *lpo
! 630: )
! 631: {
! 632: WSABUF wsabuf;
! 633: DWORD Flags;
! 634: DWORD Result;
! 635:
! 636: lpo->request_type = SOCK_RECV;
! 637: lpo->recv_buf = buff;
! 638:
! 639: if (buff != NULL) {
! 640: Flags = 0;
! 641: buff->fd = s;
! 642: buff->recv_srcadr_len = sizeof(buff->recv_srcadr);
! 643: wsabuf.buf = (char *)buff->recv_buffer;
! 644: wsabuf.len = sizeof(buff->recv_buffer);
! 645:
! 646: if (SOCKET_ERROR == WSARecvFrom(buff->fd, &wsabuf, 1,
! 647: NULL, &Flags,
! 648: &buff->recv_srcadr.sa,
! 649: &buff->recv_srcadr_len,
! 650: (LPOVERLAPPED)lpo, NULL)) {
! 651: Result = GetLastError();
! 652: switch (Result) {
! 653: case NO_ERROR :
! 654: case WSA_IO_PENDING :
! 655: break ;
! 656:
! 657: case WSAENOTSOCK :
! 658: msyslog(LOG_ERR, "Can't read from non-socket fd %d: %m", (int)buff->fd);
! 659: /* return the buffer */
! 660: freerecvbuf(buff);
! 661: return 0;
! 662: break;
! 663:
! 664: case WSAEFAULT :
! 665: msyslog(LOG_ERR, "The buffers parameter is incorrect: %m");
! 666: /* return the buffer */
! 667: freerecvbuf(buff);
! 668: return 0;
! 669: break;
! 670:
! 671: default :
! 672: /* nop */ ;
! 673: }
! 674: }
! 675: }
! 676: else
! 677: return 0;
! 678: return 1;
! 679: }
! 680:
! 681:
! 682: /* Returns 0 if any Error */
! 683: static int
! 684: OnSocketRecv(ULONG_PTR i, IoCompletionInfo *lpo, DWORD Bytes, int errstatus)
! 685: {
! 686: struct recvbuf *buff = NULL;
! 687: recvbuf_t *newbuff;
! 688: l_fp arrival_time;
! 689: struct interface * inter = (struct interface *) i;
! 690:
! 691: get_systime(&arrival_time);
! 692:
! 693: NTP_REQUIRE(NULL != lpo);
! 694: NTP_REQUIRE(NULL != lpo->recv_buf);
! 695:
! 696: /*
! 697: * Convert the overlapped pointer back to a recvbuf pointer.
! 698: */
! 699: buff = lpo->recv_buf;
! 700:
! 701: /*
! 702: * If the socket is closed we get an Operation Aborted error
! 703: * Just clean up
! 704: */
! 705: if (errstatus == WSA_OPERATION_ABORTED)
! 706: {
! 707: freerecvbuf(buff);
! 708: lpo->recv_buf = NULL;
! 709: FreeHeap(lpo, "OnSocketRecv: Socket Closed");
! 710: return (1);
! 711: }
! 712:
! 713: /*
! 714: * Get a new recv buffer for the replacement socket receive
! 715: */
! 716: newbuff = get_free_recv_buffer_alloc();
! 717: QueueSocketRecv(inter->fd, newbuff, lpo);
! 718:
! 719: DPRINTF(4, ("%sfd %d %s recv packet mode is %d\n",
! 720: (MODE_BROADCAST == get_packet_mode(buff))
! 721: ? " **** Broadcast "
! 722: : "",
! 723: (int)buff->fd, stoa(&buff->recv_srcadr),
! 724: get_packet_mode(buff)));
! 725:
! 726: /*
! 727: * If we keep it add some info to the structure
! 728: */
! 729: if (Bytes && !inter->ignore_packets) {
! 730: memcpy(&buff->recv_time, &arrival_time, sizeof buff->recv_time);
! 731: buff->recv_length = (int) Bytes;
! 732: buff->receiver = receive;
! 733: buff->dstadr = inter;
! 734:
! 735: DPRINTF(2, ("Received %d bytes fd %d in buffer %p from %s\n",
! 736: Bytes, (int)buff->fd, buff, stoa(&buff->recv_srcadr)));
! 737:
! 738: packets_received++;
! 739: inter->received++;
! 740: add_full_recv_buffer(buff);
! 741: /*
! 742: * Now signal we have something to process
! 743: */
! 744: SetEvent(WaitableIoEventHandle);
! 745: } else
! 746: freerecvbuf(buff);
! 747:
! 748: return 1;
! 749: }
! 750:
! 751:
! 752: /* Add a socket handle to the I/O completion port, and send
! 753: * NTP_RECVS_PER_SOCKET recv requests to the kernel.
! 754: */
! 755: extern int
! 756: io_completion_port_add_socket(SOCKET fd, struct interface *inter)
! 757: {
! 758: IoCompletionInfo *lpo;
! 759: recvbuf_t *buff;
! 760: int n;
! 761:
! 762: if (fd != INVALID_SOCKET) {
! 763: if (NULL == CreateIoCompletionPort((HANDLE)fd,
! 764: hIoCompletionPort, (ULONG_PTR)inter, 0)) {
! 765: msyslog(LOG_ERR, "Can't add socket to i/o completion port: %m");
! 766: return 1;
! 767: }
! 768: }
! 769:
! 770: /*
! 771: * Windows 2000 bluescreens with bugcheck 0x76
! 772: * PROCESS_HAS_LOCKED_PAGES at ntpd process
! 773: * termination when using more than one pending
! 774: * receive per socket. A runtime version test
! 775: * would allow using more on newer versions
! 776: * of Windows.
! 777: */
! 778:
! 779: #define WINDOWS_RECVS_PER_SOCKET 1
! 780:
! 781: for (n = 0; n < WINDOWS_RECVS_PER_SOCKET; n++) {
! 782:
! 783: buff = get_free_recv_buffer_alloc();
! 784: lpo = (IoCompletionInfo *) GetHeapAlloc("io_completion_port_add_socket");
! 785: if (lpo == NULL)
! 786: {
! 787: msyslog(LOG_ERR, "Can't allocate heap for completion port: %m");
! 788: return 1;
! 789: }
! 790:
! 791: QueueSocketRecv(fd, buff, lpo);
! 792:
! 793: }
! 794: return 0;
! 795: }
! 796:
! 797: static int
! 798: OnWriteComplete(ULONG_PTR i, IoCompletionInfo *lpo, DWORD Bytes, int errstatus)
! 799: {
! 800: transmitbuf_t *buff;
! 801: struct interface *inter;
! 802:
! 803: UNUSED_ARG(Bytes);
! 804:
! 805: buff = lpo->trans_buf;
! 806:
! 807: free_trans_buf(buff);
! 808: lpo->trans_buf = NULL;
! 809:
! 810: if (SOCK_SEND == lpo->request_type) {
! 811: switch (errstatus) {
! 812: case WSA_OPERATION_ABORTED:
! 813: case NO_ERROR:
! 814: break;
! 815:
! 816: default:
! 817: inter = (struct interface *)i;
! 818: packets_notsent++;
! 819: inter->notsent++;
! 820: break;
! 821: }
! 822: }
! 823:
! 824: if (errstatus == WSA_OPERATION_ABORTED)
! 825: FreeHeap(lpo, "OnWriteComplete: Socket Closed");
! 826: else
! 827: FreeHeap(lpo, "OnWriteComplete");
! 828: return 1;
! 829: }
! 830:
! 831:
! 832: /*
! 833: * mimic sendto() interface
! 834: */
! 835: int
! 836: io_completion_port_sendto(
! 837: int fd,
! 838: void * pkt,
! 839: size_t len,
! 840: sockaddr_u * dest
! 841: )
! 842: {
! 843: WSABUF wsabuf;
! 844: transmitbuf_t * buff;
! 845: DWORD Result;
! 846: int errval;
! 847: int AddrLen;
! 848: IoCompletionInfo * lpo;
! 849: DWORD Flags;
! 850:
! 851: Result = ERROR_SUCCESS;
! 852: lpo = (IoCompletionInfo *)GetHeapAlloc("io_completion_port_sendto");
! 853: if (lpo == NULL) {
! 854: SetLastError(ERROR_OUTOFMEMORY);
! 855: return -1;
! 856: }
! 857:
! 858: if (len <= sizeof(buff->pkt)) {
! 859: buff = get_trans_buf();
! 860:
! 861: if (buff == NULL) {
! 862: msyslog(LOG_ERR, "No more transmit buffers left - data discarded");
! 863: FreeHeap(lpo, "io_completion_port_sendto");
! 864: SetLastError(ERROR_OUTOFMEMORY);
! 865: return -1;
! 866: }
! 867:
! 868: memcpy(&buff->pkt, pkt, len);
! 869: wsabuf.buf = buff->pkt;
! 870: wsabuf.len = len;
! 871:
! 872: AddrLen = SOCKLEN(dest);
! 873: lpo->request_type = SOCK_SEND;
! 874: lpo->trans_buf = buff;
! 875: Flags = 0;
! 876:
! 877: Result = WSASendTo(fd, &wsabuf, 1, NULL, Flags,
! 878: &dest->sa, AddrLen,
! 879: (LPOVERLAPPED)lpo, NULL);
! 880: if (Result == SOCKET_ERROR) {
! 881: errval = WSAGetLastError();
! 882: switch (errval) {
! 883:
! 884: case NO_ERROR :
! 885: case WSA_IO_PENDING :
! 886: Result = ERROR_SUCCESS;
! 887: break ;
! 888:
! 889: /*
! 890: * Something bad happened
! 891: */
! 892: default :
! 893: msyslog(LOG_ERR,
! 894: "WSASendTo(%s) error %d: %s",
! 895: stoa(dest), errval, strerror(errval));
! 896: free_trans_buf(buff);
! 897: lpo->trans_buf = NULL;
! 898: FreeHeap(lpo, "io_completion_port_sendto");
! 899: break;
! 900: }
! 901: }
! 902: #ifdef DEBUG
! 903: if (debug > 3)
! 904: printf("WSASendTo - %d bytes to %s : %d\n", len, stoa(dest), Result);
! 905: #endif
! 906: if (ERROR_SUCCESS == Result)
! 907: return len;
! 908: SetLastError(Result);
! 909: return -1;
! 910: } else {
! 911: #ifdef DEBUG
! 912: if (debug) printf("Packet too large: %d Bytes\n", len);
! 913: #endif
! 914: SetLastError(ERROR_INSUFFICIENT_BUFFER);
! 915: return -1;
! 916: }
! 917: }
! 918:
! 919:
! 920: /*
! 921: * async_write, clone of write(), used by some reflock drivers
! 922: */
! 923: int
! 924: async_write(
! 925: int fd,
! 926: const void *data,
! 927: unsigned int count
! 928: )
! 929: {
! 930: transmitbuf_t *buff;
! 931: IoCompletionInfo *lpo;
! 932: DWORD BytesWritten;
! 933:
! 934: if (count > sizeof buff->pkt) {
! 935: #ifdef DEBUG
! 936: if (debug) {
! 937: printf("async_write: %d bytes too large, limit is %d\n",
! 938: count, sizeof buff->pkt);
! 939: exit(-1);
! 940: }
! 941: #endif
! 942: errno = ENOMEM;
! 943: return -1;
! 944: }
! 945:
! 946: buff = get_trans_buf();
! 947: lpo = (IoCompletionInfo *) GetHeapAlloc("async_write");
! 948:
! 949: if (! buff || ! lpo) {
! 950: if (buff) {
! 951: free_trans_buf(buff);
! 952: DPRINTF(1, ("async_write: out of memory\n"));
! 953: } else
! 954: msyslog(LOG_ERR, "No more transmit buffers left - data discarded");
! 955:
! 956: errno = ENOMEM;
! 957: return -1;
! 958: }
! 959:
! 960: lpo->request_type = SERIAL_WRITE;
! 961: lpo->trans_buf = buff;
! 962: memcpy(&buff->pkt, data, count);
! 963:
! 964: if (!WriteFile((HANDLE)_get_osfhandle(fd), buff->pkt, count,
! 965: &BytesWritten, (LPOVERLAPPED)lpo)
! 966: && ERROR_IO_PENDING != GetLastError()) {
! 967:
! 968: msyslog(LOG_ERR, "async_write - error %m");
! 969: free_trans_buf(buff);
! 970: lpo->trans_buf = NULL;
! 971: FreeHeap(lpo, "async_write");
! 972: errno = EBADF;
! 973: return -1;
! 974: }
! 975:
! 976: return count;
! 977: }
! 978:
! 979:
! 980: /*
! 981: * GetReceivedBuffers
! 982: * Note that this is in effect the main loop for processing requests
! 983: * both send and receive. This should be reimplemented
! 984: */
! 985: int GetReceivedBuffers()
! 986: {
! 987: isc_boolean_t have_packet = ISC_FALSE;
! 988: while (!have_packet) {
! 989: DWORD Index = WaitForMultipleObjects(MAXHANDLES, WaitHandles, FALSE, INFINITE);
! 990: switch (Index) {
! 991: case WAIT_OBJECT_0 + 0 : /* Io event */
! 992: # ifdef DEBUG
! 993: if ( debug > 3 )
! 994: {
! 995: printf( "IoEvent occurred\n" );
! 996: }
! 997: # endif
! 998: have_packet = ISC_TRUE;
! 999: break;
! 1000: case WAIT_OBJECT_0 + 1 : /* exit request */
! 1001: exit(0);
! 1002: break;
! 1003: case WAIT_OBJECT_0 + 2 : /* timer */
! 1004: timer();
! 1005: break;
! 1006: case WAIT_IO_COMPLETION : /* loop */
! 1007: case WAIT_TIMEOUT :
! 1008: break;
! 1009: case WAIT_FAILED:
! 1010: msyslog(LOG_ERR, "ntpd: WaitForMultipleObjects Failed: Error: %m");
! 1011: break;
! 1012:
! 1013: /* For now do nothing if not expected */
! 1014: default:
! 1015: break;
! 1016:
! 1017: } /* switch */
! 1018: }
! 1019:
! 1020: return (full_recvbuffs()); /* get received buffers */
! 1021: }
! 1022:
! 1023: #else
! 1024: static int NonEmptyCompilationUnit;
! 1025: #endif
! 1026:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>