From 294e9705f22600eaba0f6a2875b275c3039c3b0f Mon Sep 17 00:00:00 2001 From: "B. Scott Michel" Date: Wed, 11 Dec 2024 11:40:36 -0800 Subject: [PATCH] SCP: NAT poll() optimizations - Manage poll() file descriptors more efficiently. Both Linux() poll and Windows WSAPoll() ignore file descriptors that are negative in the pollfd array. Set the pollfd.fd file descriptor to INVALID_SOCKET when no longer needed. Reuse the pollfd array element if have_valid_socket() returns false. - sim_slirp_dispatch(): Removed, empty function no longer required. - sim_deb: Remove code that NULLs this FILE pointer when "flushing" the debug output. Presumably, closing the file and re-opening it flushes the debug output. Synchronizing when this operation occurs with other code in other threads also write to sim_deb needs to be revisited. For the time being, just fflush(sim_deb). Otherwise, _sim_debug_write_all() gets stuck in an infinite loop writing zero bytes. --- CMakeLists.txt | 6 + cmake/add_simulator.cmake | 4 +- cmake/cmake-builder.ps1 | 49 ++++++-- cmake/cmake-builder.sh | 14 ++- cmake/os-features.cmake | 72 ++++++------ cmake/platform-quirks.cmake | 33 +++++- libslirp | 2 +- scp.c | 74 ++++++------ sim_console.c | 6 +- sim_debtab.c | 19 ++-- sim_ether.c | 1 - sim_slirp/sim_slirp.c | 27 ++--- sim_slirp/sim_slirp.h | 1 - sim_slirp/sim_slirp_network.h | 20 ++-- sim_slirp/slirp_poll.c | 206 +++++++++++++++++++--------------- 15 files changed, 316 insertions(+), 218 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1a893bec3..fa469bdf0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -226,6 +226,12 @@ option(SIMH_PACKAGE_SUFFIX option(MAC_UNIVERSAL "macOS universal binary flag: TRUE -> build universal binaries, FALSE -> don't." ${MAC_UNIVERSAL_OPTVAL}) +option(USE_SELECT + "NAT(libslirp): Use select() as the socket polling function" + FALSE) +option(USE_POLL + "NAT(libslirp): Use poll() as the socket polling function" + FALSE) # Places where CMake should look for dependent package configuration fragments and artifacts: set(SIMH_PREFIX_PATH_LIST) diff --git a/cmake/add_simulator.cmake b/cmake/add_simulator.cmake index 06e16639c..4fe6288ca 100644 --- a/cmake/add_simulator.cmake +++ b/cmake/add_simulator.cmake @@ -99,8 +99,8 @@ function(build_simcore _targ) ${CMAKE_SOURCE_DIR}/libslirp/src ${CMAKE_BINARY_DIR}/libslirp/build-include ) - endif () - + endif () + target_link_options(${lib} PRIVATE ${EXTRA_TARGET_LFLAGS}) # Make sure that the top-level directory is part of the libary's include path: diff --git a/cmake/cmake-builder.ps1 b/cmake/cmake-builder.ps1 index 5fa876348..b7416873c 100644 --- a/cmake/cmake-builder.ps1 +++ b/cmake/cmake-builder.ps1 @@ -72,9 +72,9 @@ param ( ## ## Supported flavors: ## ------------------ - ## vs2022 Visual Studio 2022 (default) + ## vs2022 Visual Studio 2022 ## vs2022-xp Visual Studio 2022 XP compat - ## vs2022-x64 Visual Studio 2022 64-bit + ## vs2022-x64 Visual Studio 2022 64-bit (default) ## vs2019 Visual Studio 2019 ## vs2019-xp Visual Studio 2019 XP compat ## vs2019-x64 Visual Studio 2019 64-bit @@ -166,6 +166,14 @@ param ( [Parameter(Mandatory=$false)] [string] $sanitizer = "", + ## Use select() as the NAT(libslirp) socket polling function + [Parameter(Mandatory=$false)] + [switch] $use_select = $false, + + ## Use WSAPoll() as the NAT(libslirp) socket polling function + [Parameter(Mandatory=$false)] + [switch] $use_poll = $false, + ## Turn on maximal compiler warnings for Debug builds (e.g. "-Wall" or "/W3") [Parameter(Mandatory=$false)] [switch] $debugWall = $false, @@ -207,7 +215,7 @@ class GeneratorInfo ## Multiple build configurations selected at compile time $multiConfig = $false ## Single configuration selected at configuration time -$singleConfig = $true +$singleConfig = $True $cmakeGenMap = @{ "vs2022" = [GeneratorInfo]::new("Visual Studio 17 2022", $multiConfig, $false, "", @("-A", "Win32")); @@ -391,6 +399,15 @@ else } } +## Can only have one of use_select or use_poll +if ($use_select -and $use_poll) +{ + Write-Host "" + Write-Host "!! ${scriptName}: Can only set one of -use_select, -use_poll" + Write-Host "" + exit 0 +} + if (($scriptPhases -contains "generate") -or ($scriptPhases -contains "build")) { ## Clean out the build subdirectory @@ -457,16 +474,26 @@ if (($scriptPhases -contains "generate") -or ($scriptPhases -contains "build")) { $generateArgs += @("-DSIMH_PACKAGE_SUFFIX:Bool=${cpack_suffix}") } + if ($use_select) + { + $generateArgs += @("-DUSE_SELECT:Bool=True") + } + if ($use_poll) + { + $generateArgs += @("-DUSE_POLL:Bool=True") + } ## Add sanitizer(s) - foreach ($santhing in $sanitizer.Split(',')) { - switch -exact ($santhing) - { - "address" { - $generateArgs += @("-DSANITIZE_ADDRESS:Bool=On") - } - default { - Write-Host "** Unknown sanitizer option: ${santhing}, ignoring" + if (![String]::IsNullOrEmpty($sanitizer)) { + foreach ($santhing in $sanitizer.Split(',')) { + switch -exact ($santhing) + { + "address" { + $generateArgs += @("-DSANITIZE_ADDRESS:Bool=On") + } + default { + Write-Host "** Unknown sanitizer option: ${santhing}, ignoring" + } } } } diff --git a/cmake/cmake-builder.sh b/cmake/cmake-builder.sh index a52a1df85..281e5a3bc 100755 --- a/cmake/cmake-builder.sh +++ b/cmake/cmake-builder.sh @@ -41,6 +41,10 @@ Configure and build simh simulators on Linux and *nix-like platforms. memory [MSan -- memory sanitizer] thread [TSan -- thread sanitizer] undef [UBSan -- undefined behavior sanitizer] +--use-select Use select() as the NAT(libslirp) socket polling + function +--use-poll Use poll() as the NAT(libslirp) socket polling + function, if available. --cpack_suffix Specify CPack's packaging suffix, e.g., "ubuntu-22.04" to produce the "simh-4.1.0-ubuntu-22.04.deb" Debian @@ -169,7 +173,7 @@ fi longopts=clean,help,flavor:,config:,nonetwork,novideo,notest,parallel,generate,testonly longopts=${longopts},noinstall,installonly,verbose,target:,lto,debugWall,cppcheck,cpack_suffix: -longopts=${longopts},cache,no-aio,no-aio-intrinsics,sanitizer: +longopts=${longopts},cache,no-aio,no-aio-intrinsics,sanitizer:,use-select,use-poll ARGS=$(${getopt_prog} --longoptions $longopts --options xhf:c:pg -- "$@") if [ $? -ne 0 ] ; then @@ -313,6 +317,14 @@ while true; do esac shift 2 ;; + --use-select) + generateArgs="${generateArgs} -DUSE_SELECT:Bool=True" + shift + ;; + --use-poll) + generateArgs="${generateArgs} -DUSE_POLL:Bool=True" + shift + ;; --) ## End of options. we'll ignore. shift diff --git a/cmake/os-features.cmake b/cmake/os-features.cmake index 44b05afa3..654831522 100644 --- a/cmake/os-features.cmake +++ b/cmake/os-features.cmake @@ -182,20 +182,6 @@ if (WITH_NETWORK) endif (WITH_TAP) endif (WITH_NETWORK) -## Windows: winmm (for ms timer functions), socket functions (even when networking is -## disabled. Also squelch the deprecation warnings (these warnings can be enabled -## via the -DWINAPI_DEPRECATION:Bool=On flag at configure time.) -if (WIN32) - target_link_libraries(os_features INTERFACE ws2_32 wsock32 winmm) - target_compile_definitions(os_features INTERFACE HAVE_WINMM) - if (NOT WINAPI_DEPRECATION) - target_compile_definitions(os_features INTERFACE - _CRT_NONSTDC_NO_WARNINGS - _CRT_SECURE_NO_WARNINGS - ) - endif () -endif () - ## Cygwin also wants winmm. Note: Untested but should work. if (CYGWIN) check_library_exists(winmm timeGetTime "" HAS_WINMM) @@ -215,31 +201,53 @@ endif () set(sim_use_select 0) set(sim_use_poll 0) -if (NOT WIN32) - check_include_file(poll.h have_poll_h) - if (have_poll_h) - set(sim_use_poll 1) - else () - set(sim_use_select 1) - endif () -else () - cmake_push_check_state() - list(APPEND CMAKE_REQUIRED_LIBRARIES "ws2_32") +set(check_poll 0) - check_symbol_exists(WSAPoll "winsock2.h;windows.h" have_wsa_poll) - if (have_wsa_poll) - set(sim_use_poll 1) +if (USE_SELECT) + set(sim_use_select 1) +else () + if (NOT WIN32) + check_symbol_exists(poll "poll.h" have_poll_h) + if (have_poll_h) + set(sim_use_poll 1) + elseif (USE_POLL) + message(FATAL_ERROR "USE_POLL set, poll.h not detected. Select a different socket polling mechansim.") + else () + set(sim_use_select 1) + endif() else () - set(sim_use_select 1) - endif () + cmake_push_check_state() + list(APPEND CMAKE_REQUIRED_LIBRARIES "ws2_32" "wsock32") - cmake_pop_check_state() -endif() + check_symbol_exists(WSAPoll "winsock2.h;windows.h" have_wsa_poll) + if (have_wsa_poll) + set(sim_use_poll 1) + else () + set(sim_use_select 1) + endif () + + cmake_pop_check_state() + endif() +endif () -target_compile_definitions(os_features INTERFACE +target_compile_definitions(os_features INTERFACE SIM_USE_POLL=${sim_use_poll} SIM_USE_SELECT=${sim_use_select} ) +## Windows: winmm (for ms timer functions), socket functions (even when networking is +## disabled. Also squelch the deprecation warnings (these warnings can be enabled +## via the -DWINAPI_DEPRECATION:Bool=On flag at configure time.) +if (WIN32) + target_link_libraries(os_features INTERFACE ws2_32 wsock32 winmm) + target_compile_definitions(os_features INTERFACE HAVE_WINMM) + if (NOT WINAPI_DEPRECATION) + target_compile_definitions(os_features INTERFACE + _CRT_NONSTDC_NO_WARNINGS + _CRT_SECURE_NO_WARNINGS + ) + endif () +endif () + ## Sanitizer support find_package(Sanitizers) diff --git a/cmake/platform-quirks.cmake b/cmake/platform-quirks.cmake index 3c3b19b31..b6dd00def 100644 --- a/cmake/platform-quirks.cmake +++ b/cmake/platform-quirks.cmake @@ -132,6 +132,37 @@ if (WIN32) message(STATUS "Target Windows version set to ${TARGET_WINVER}") add_compile_definitions(WINVER=${TARGET_WINVER} _WIN32_WINNT=${TARGET_WINVER}) list(APPEND CMAKE_REQUIRED_DEFINITIONS "-DWINVER=${TARGET_WINVER}" "-D_WIN32_WINNT=${TARGET_WINVER}") + set(WINVER ${TARGET_WINVER}) + else () + file(WRITE + ${CMAKE_BINARY_DIR}/CMakeTmp/testWinVer.c + "#define WINDOWS_LEAN_AND_MEAN\n" + "#include \n" + "#include \n" + "int main(void) {\n" + "#if defined(WINVER) && WINVER >= 0x0601\n" + " return 0;\n" + "#else\n" + " return 1;\n" + "#endif\n" + "}\n" + ) + + try_run(RUN_WINVER COMPILE_WINVER + SOURCES ${CMAKE_BINARY_DIR}/CMakeTmp/testWinVer.c + RUN_OUTPUT_VARIABLE THE_WINVER + ) + + if (RUN_WINVER EQUAL 0) + message(STATUS "Windows Vista or later.") + set(WINDOWS_VISTA_PLUS TRUE) + else () + message(STATUS "Windows version not detected: run status ${RUN_WINVER}, compile status ${COMPILE_WINVER}") + set(WINDOWS_VISTA_PLUS FALSE) + endif () + + unset(RUN_WINVER) + unset(COMPILE_WINVER) endif () elseif (${CMAKE_SYSTEM_NAME} MATCHES "Linux") # The MSVC solution builds as 32-bit, but none of the *nix platforms do. @@ -223,8 +254,6 @@ if (CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID MATCHES ".*Clang") string(REGEX REPLACE "${opt_flag}[ \t\r\n]*" "" CMAKE_C_FLAGS_MINSIZEREL "${CMAKE_C_FLAGS_MINSIZEREL}") string(APPEND CMAKE_C_FLAGS_MINSIZEREL " ${opt_flag}") endforeach () -else () - message(STATUS "Not changing CMAKE_C_FLAGS_RELEASE on ${CMAKE_C_COMPILER_ID}") endif () diff --git a/libslirp b/libslirp index c607b93c7..2853aab5d 160000 --- a/libslirp +++ b/libslirp @@ -1 +1 @@ -Subproject commit c607b93c7c2ce6b5242be436a3d53e531bdf358b +Subproject commit 2853aab5dbe0f3c46dab6bf69957607534d622c7 diff --git a/scp.c b/scp.c index 0fffb76be..2257fa36f 100644 --- a/scp.c +++ b/scp.c @@ -13621,14 +13621,32 @@ static void _debug_fwrite_all (const char *buf, size_t len, FILE *f) { size_t len_written; +AIO_DEBUG_LOCK; + while (len > 0) { errno = 0; len_written = fwrite (buf, 1, len, f); - len -= len_written; - buf += len_written; - if (errno == EAGAIN) /* Non blocking file descriptor buffer full? */ - sim_os_ms_sleep(10);/* wait a bit to retry */ + if (len_written > 0) { + len -= len_written; + buf += len_written; + } else { + if (errno == EAGAIN) /* Non blocking file descriptor buffer full? */ + sim_os_ms_sleep(10);/* wait a bit to retry */ + else + break; + } + } + +/* errno was not EAGAIN. Highly possible that sim_deb is now NULL */ +if (len > 0) { + fputs("Unable to write debug log, buf reminaing:\n", stdout); + fputs(buf, stdout); + fputs("\r\n", stdout); + printf("errno %d (%s)\r\n", errno, strerror(errno)); + fflush(stdout); } + +AIO_DEBUG_UNLOCK; } static void _debug_fwrite (const char *buf, size_t len) @@ -13670,7 +13688,6 @@ if (sim_deb_switches & SWMASK ('F')) { /* filtering disabled? */ _debug_fwrite (buf, len); /* output now. */ return; /* done */ } -AIO_DEBUG_LOCK; if (debug_line_offset + len + 1 > debug_line_bufsize) { /* realloc(NULL, size) == malloc(size). Initialize the malloc()-ed space. Only need to test debug_line_buf since SIMH allocates both buffers at the same @@ -13755,7 +13772,6 @@ while (NULL != (eol = strchr (debug_line_buf, '\n')) || flush) { memmove (debug_line_buf, eol + 1, debug_line_offset); debug_line_buf[debug_line_offset] = '\0'; } -AIO_DEBUG_UNLOCK; } static void _sim_debug_write (const char *buf, size_t len) @@ -13765,33 +13781,11 @@ _sim_debug_write_flush (buf, len, FALSE); static t_stat _sim_debug_flush (void) { -int32 saved_quiet = sim_quiet; -int32 saved_sim_switches = sim_switches; -int32 saved_deb_switches = sim_deb_switches; -struct timespec saved_deb_basetime = sim_deb_basetime; -char saved_debug_filename[CBUFSIZE]; - -if (sim_deb == NULL) /* no debug? */ - return SCPE_OK; - -_sim_debug_write_flush ("", 0, TRUE); - -if (sim_deb == sim_log) { /* debug is log */ - fflush (sim_deb); /* fflush is the best we can do */ - return SCPE_OK; +if (sim_deb != NULL) { /* no debug? */ + _sim_debug_write_flush ("", 0, TRUE); + fflush(sim_deb); } -if (!(saved_deb_switches & SWMASK ('B'))) { - strcpy (saved_debug_filename, sim_logfile_name (sim_deb, sim_deb_ref)); - - sim_quiet = 1; - sim_set_deboff (0, NULL); - sim_switches = saved_deb_switches; - sim_set_debon (0, saved_debug_filename); - sim_deb_basetime = saved_deb_basetime; - sim_switches = saved_sim_switches; - sim_quiet = saved_quiet; - } return SCPE_OK; } @@ -13830,8 +13824,7 @@ char tim_t[32] = ""; char tim_a[32] = ""; char pc_s[MAX_WIDTH + 1] = ""; struct timespec time_now; - -AIO_DEBUG_LOCK; +const size_t n_dbg_prefix = sizeof(debug_line_prefix) / sizeof(char); if (sim_deb_switches & (SWMASK ('T') | SWMASK ('R') | SWMASK ('A'))) { sim_rtcn_get_time(&time_now, 0); @@ -13844,7 +13837,10 @@ if (sim_deb_switches & (SWMASK ('T') | SWMASK ('R') | SWMASK ('A'))) { sprintf(tim_t, "%02d:%02d:%02d.%03d ", now->tm_hour, now->tm_min, now->tm_sec, (int)(time_now.tv_nsec/1000000)); } if (sim_deb_switches & SWMASK ('A')) { - sprintf(tim_t, "%" LL_FMT "d.%03d ", (LL_TYPE)(time_now.tv_sec), (int)(time_now.tv_nsec/1000000)); + const size_t n_tim_t = sizeof(tim_t) / sizeof(char); + + snprintf(tim_t, n_tim_t, "%" LL_FMT "d.%03d ", (LL_TYPE) time_now.tv_sec, (int) (time_now.tv_nsec/1000000)); + tim_t[n_tim_t - 1] = '\0'; } } if (sim_deb_switches & SWMASK ('P')) { @@ -13864,8 +13860,9 @@ if (sim_deb_switches & SWMASK ('P')) { sprint_val (&pc_s[strlen(pc_s)], val, sim_PC->radix, sim_PC->width, sim_PC->flags & REG_FMT); } -sprintf(debug_line_prefix, "DBG(%s%s%.0f%s)%s> %s %s: ", tim_t, tim_a, sim_gtime(), pc_s, AIO_MAIN_THREAD ? "" : "+", dev_name, debug_type); -AIO_DEBUG_UNLOCK; +snprintf(debug_line_prefix, n_dbg_prefix, "DBG(%s%s%.0f%s)%s> %s %s: ", + tim_t, tim_a, sim_gtime(), pc_s, AIO_MAIN_THREAD ? "" : "+", dev_name, debug_type); +debug_line_prefix[n_dbg_prefix - 1] = '\0'; return debug_line_prefix; } @@ -14217,7 +14214,10 @@ if (sim_deb && dptr && ((dptr->dctrl | (uptr ? uptr->dctrl : 0)) & dbits)) { void sim_misc_debug (const char *debug_thing, const char *debug_type, const char* msg) { if (sim_deb != NULL) { - char *buf = strdup(msg); + const size_t msg_len = strlen(msg); + char *buf = (char *) calloc(msg_len + 4, sizeof(char)); + + memcpy(buf, msg, msg_len); AIO_DEBUG_LOCK; sim_debug_expand_newlines(buf, strlen(buf), sim_debug_prefix(debug_thing, debug_type)); diff --git a/sim_console.c b/sim_console.c index d159862a0..bc3a1bf0e 100644 --- a/sim_console.c +++ b/sim_console.c @@ -369,7 +369,7 @@ static SHTAB show_con_tab[] = { { "WRU", &sim_show_kmap, KMAP_WRU }, { "BRK", &sim_show_kmap, KMAP_BRK }, { "DEL", &sim_show_kmap, KMAP_DEL }, -#if (defined(__GNUC__) && !defined(__OPTIMIZE__) && !defined(_WIN32)) /* Debug build? */ +#if (defined(__GNUC__) /*&& !defined(__OPTIMIZE__)*/ && !defined(_WIN32)) /* Debug build? */ { "DBGINT", &sim_show_kmap, KMAP_DBGINT }, #endif { "PCHAR", &sim_show_pchar, 0 }, @@ -3801,7 +3801,7 @@ static t_stat sim_os_ttrun (void) { sim_debug (DBG_TRC, &sim_con_telnet, "sim_os_ttrun() - BSDTTY\n"); -#if (defined(__GNUC__) && !defined(__OPTIMIZE__)) /* Debug build? */ +#if (defined(__GNUC__) /*&& !defined(__OPTIMIZE__))*/ /* Debug build? */ if (sim_dbg_int_char == 0) sim_dbg_int_char = sim_int_char + 1; runtchars.t_intrc = sim_dbg_int_char; /* let debugger get SIGINT with next highest char */ @@ -3984,7 +3984,7 @@ runtty.c_cc[VINTR] = 0; /* OS X doesn't deliver #else runtty.c_cc[VINTR] = sim_int_char; /* in case changed */ #endif -#if (defined(__GNUC__) && !defined(__OPTIMIZE__)) /* Debug build? */ +#if (defined(__GNUC__) /*&& !defined(__OPTIMIZE__)*/) /* Debug build? */ if (sim_dbg_int_char == 0) sim_dbg_int_char = sim_int_char + 1; runtty.c_cc[VINTR] = sim_dbg_int_char; /* let debugger get SIGINT with next highest char */ diff --git a/sim_debtab.c b/sim_debtab.c index 3890d3544..d7ac6fb39 100644 --- a/sim_debtab.c +++ b/sim_debtab.c @@ -70,7 +70,8 @@ void sim_fill_debtab_flags(DEBTAB *debtab) { uint32_t dbg_bitmask = 0, new_flag; DEBTAB *p; - int i; + const unsigned int n_bits = sizeof(dbg_bitmask) * 8; + unsigned int i; /* Augment the device's debugging table with the poll and socket flags, * so that debugging looks correct in the output (see _get_dbg_verb() in @@ -79,14 +80,11 @@ void sim_fill_debtab_flags(DEBTAB *debtab) for (p = debtab; p->name != NULL; ++p) dbg_bitmask |= p->mask; - for (p = debtab, new_flag = 1, i = sizeof(dbg_bitmask) * 8; p->name != NULL && i > 0; /* empty */) { - while (i > 0 && (new_flag & dbg_bitmask) != 0) { - new_flag <<= 1; - --i; - } - - /* Found a hole in the existing debugging flags */ - if (i > 0) { + p = debtab; + new_flag = 1; + i = 0; + while (p->name != NULL && i < n_bits) { + if ((new_flag & dbg_bitmask) == 0) { /* Find a need for the new flag */ while (p->mask != 0 && p->name != NULL) ++p; @@ -94,5 +92,8 @@ void sim_fill_debtab_flags(DEBTAB *debtab) p->mask = new_flag; } } + + new_flag <<= 1; + ++i; } } diff --git a/sim_ether.c b/sim_ether.c index b530b5849..3a6f2abf2 100644 --- a/sim_ether.c +++ b/sim_ether.c @@ -2077,7 +2077,6 @@ while (dev->handle) { #endif /* HAVE_VDE_NETWORK */ #ifdef HAVE_SLIRP_NETWORK case ETH_API_NAT: - sim_slirp_dispatch ((SimSlirpNetwork *) dev->handle); status = 1; break; #endif /* HAVE_SLIRP_NETWORK */ diff --git a/sim_slirp/sim_slirp.c b/sim_slirp/sim_slirp.c index b684cd0bb..54b30d222 100644 --- a/sim_slirp/sim_slirp.c +++ b/sim_slirp/sim_slirp.c @@ -518,8 +518,10 @@ void sim_slirp_close(SimSlirpNetwork *slirp) #if SIM_USE_SELECT free(slirp->lut); -#else + slirp->lut = NULL; +#elif SIM_USE_POLL free(slirp->fds); + slirp->fds = NULL; #endif pthread_mutex_destroy(&slirp->libslirp_access); @@ -584,9 +586,9 @@ t_stat sim_slirp_attach_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, con /* Initialize the select/poll file descriptor arrays. */ static int initialize_poll_fds(SimSlirpNetwork *slirp) { -#if SIM_USE_SELECT size_t i; +#if SIM_USE_SELECT FD_ZERO(&slirp->readfds); FD_ZERO(&slirp->writefds); FD_ZERO(&slirp->exceptfds); @@ -597,11 +599,16 @@ static int initialize_poll_fds(SimSlirpNetwork *slirp) for (i = 0; i < slirp->lut_alloc; ++i) slirp->lut[i] = INVALID_SOCKET; -#else +#elif SIM_USE_POLL /* poll()-based file descriptor polling. */ + static const sim_pollfd_t poll_initializer = { INVALID_SOCKET, 0, 0}; + slirp->n_fds = FDS_ALLOC_INIT; slirp->fd_idx = 0; - slirp->fds = (sim_pollfd_t *) calloc(slirp->n_fds, sizeof(sim_pollfd_t)); + slirp->fds = (sim_pollfd_t *) malloc(slirp->n_fds * sizeof(sim_pollfd_t)); + for (i = 0; i < slirp->n_fds; ++i) { + slirp->fds[i] = poll_initializer; + } #endif return 0; @@ -698,18 +705,6 @@ static void notify_callback(void *opaque) GLIB_UNUSED_PARAM(opaque); } -void sim_slirp_dispatch(SimSlirpNetwork *slirp) -{ - /* Outbound packets directly to slirp_input() and packets going to the - * simulator are handled in sim_slirp_select(). - * - * Packet traffic between the simulator and libslirp is handled in - * sim_slirp_select(). - */ - - GLIB_UNUSED_PARAM(slirp); -} - int64_t sim_clock_get_ns(void *opaque) { GLIB_UNUSED_PARAM(opaque); diff --git a/sim_slirp/sim_slirp.h b/sim_slirp/sim_slirp.h index f7a3aa068..1d0fb8403 100644 --- a/sim_slirp/sim_slirp.h +++ b/sim_slirp/sim_slirp.h @@ -14,7 +14,6 @@ SimSlirpNetwork *sim_slirp_open (const char *args, void *pkt_opaque, packet_call void sim_slirp_close (SimSlirpNetwork *slirp); int sim_slirp_send (SimSlirpNetwork *slirp, const char *msg, size_t len, int flags); int sim_slirp_select (SimSlirpNetwork *slirp, int ms_timeout); -void sim_slirp_dispatch (SimSlirpNetwork *slirp); t_stat sim_slirp_attach_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); void sim_slirp_show (SimSlirpNetwork *slirp, FILE *st); diff --git a/sim_slirp/sim_slirp_network.h b/sim_slirp/sim_slirp_network.h index c8afa8708..f2636fb1b 100644 --- a/sim_slirp/sim_slirp_network.h +++ b/sim_slirp/sim_slirp_network.h @@ -36,17 +36,15 @@ # error "sim_slirp.c: Configuration error: set one of SIM_USE_SELECT, SIM_USE_POLL to 1." #endif -/* Abstract the poll structure as sim_pollfd_t */ #if SIM_USE_POLL -# if (defined(_WIN32) || defined(_WIN64)) - typedef WSAPOLLFD sim_pollfd_t; -# else +/* Abstract the poll structure as sim_pollfd_t */ +# if !defined(_WIN32) && !defined(_WIN64) # include typedef struct pollfd sim_pollfd_t; +# else + typedef WSAPOLLFD sim_pollfd_t; # endif -#endif - -#if SIM_USE_SELECT +#elif SIM_USE_SELECT #define SIM_INVALID_MAX_FD ((slirp_os_socket) -1) #endif @@ -95,15 +93,13 @@ struct sim_slirp { /* Lookup table: */ slirp_os_socket *lut; size_t lut_alloc; -#else - /* More generally, this is for poll(). */ - - /* Poll file descriptor array */ - sim_pollfd_t *fds; +#elif SIM_USE_POLL /* Next descriptor to use */ size_t fd_idx; /* Total allocated descriptors */ size_t n_fds; + /* Poll file descriptor array */ + sim_pollfd_t *fds; #endif /* SIMH debug info: */ diff --git a/sim_slirp/slirp_poll.c b/sim_slirp/slirp_poll.c index ef7e0def6..46cc18bfb 100644 --- a/sim_slirp/slirp_poll.c +++ b/sim_slirp/slirp_poll.c @@ -54,7 +54,7 @@ # if defined(PRIu32) # define SIM_PRIsocket PRIu32 # else -# define SIM_PRIsocket "lu" +# define SIM_PRIsocket "u" # endif #else # define SIM_PRIsocket "u" @@ -70,26 +70,27 @@ static void initialize_poll(SimSlirpNetwork *slirp, uint32 tmo, struct timeval * static int do_poll(SimSlirpNetwork *slirp, int ms_timeout, struct timeval *timeout); static void report_error(SimSlirpNetwork *slirp); +/* Retrieve the debug mask for an element in the augmented debug table: */ static inline uint32_t slirp_dbg_mask(const SimSlirpNetwork *slirp, size_t flag) { return (slirp != NULL && slirp->dptr != NULL) ? slirp->dptr->debflags[slirp->flag_offset + flag].mask : 0; } -#if SIM_USE_SELECT -/* Socket identifier pretty-printer: */ -static const char *print_socket(slirp_os_socket s) +/* Socket error check */ +static inline int have_valid_socket(slirp_os_socket s) { - static char retbuf[64]; - -#if defined(_WIN64) - sprintf(retbuf, "%llu", s); -#elif defined(_WIN32) - sprintf(retbuf, "%u", s); +#if !defined(_WIN32) && !defined(_WIN64) + return (s >= 0); #else - sprintf(retbuf, "%d", s); + return (s != SLIRP_INVALID_SOCKET); #endif +} - return retbuf; +#if (defined(_WIN32) || defined(_WIN64)) && SIM_USE_POLL +/* poll() wrapper for Windows: */ +static inline int poll(WSAPOLLFD *fds, size_t n_fds, int timeout) +{ + return WSAPoll(fds, (ULONG) n_fds, timeout); } #endif @@ -145,13 +146,6 @@ int sim_slirp_select(SimSlirpNetwork *slirp, int ms_timeout) * "Protocol" functions. These functions abide by the one function definition rule. *~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=*/ -#if (defined(_WIN32) || defined(_WIN64)) && SIM_USE_POLL -static inline int poll(WSAPOLLFD *fds, size_t n_fds, int timeout) -{ - return WSAPoll(fds, (ULONG) n_fds, timeout); -} -#endif - static void initialize_poll(SimSlirpNetwork *slirp, uint32 tmo, struct timeval *tv) { #if SIM_USE_SELECT @@ -164,9 +158,7 @@ static void initialize_poll(SimSlirpNetwork *slirp, uint32 tmo, struct timeval * slirp->max_fd = SIM_INVALID_MAX_FD; #elif SIM_USE_POLL - /* Reinitialize and reset */ - memset(slirp->fds, 0, slirp->fd_idx * sizeof(sim_pollfd_t)); - slirp->fd_idx = 0; + /* Nothing to do. */ #endif } @@ -202,11 +194,35 @@ static int do_poll(SimSlirpNetwork *slirp, int ms_timeout, struct timeval *timeo "poll()-ing for %" SIZE_T_FMT "u sockets\n", slirp->fd_idx); retval = poll(slirp->fds, slirp->fd_idx, ms_timeout); - if (retval > 0) - sim_debug(slirp_dbg_mask(slirp, DBG_POLL), slirp->dptr, "do_poll(): poll() returned %d\n", retval); + if (retval > 0) { + uint32_t dbg_mask = slirp_dbg_mask(slirp, DBG_POLL); + + if (sim_deb != NULL && slirp->dptr != NULL && (slirp->dptr->dctrl & dbg_mask)) { + size_t i; + + AIO_DEBUG_LOCK; + fprintf(sim_deb, "do_poll(): poll() returns %d\n", retval); + for (i = 0; i < (size_t) slirp->fd_idx; ++i) { + fprintf(sim_deb, "[%3" SIZE_T_FMT "u] fd: %04" SIM_PRIsocket ", events: %04x\n", + i, slirp->fds[i].fd, slirp->fds[i].events); + } + + fflush(sim_deb); + AIO_DEBUG_UNLOCK; + } + } } #endif + if (retval < 0) { + switch (errno) { + case EINTR: + case EAGAIN: + /* Interrupted? Do it again. */ + return do_poll(slirp, ms_timeout, timeout); + } + } + return retval; } @@ -238,10 +254,10 @@ static void report_error(SimSlirpNetwork *slirp) /* Add new socket file descriptors to the Slirp I/O event tracking state. */ void register_poll_socket(slirp_os_socket fd, void *opaque) { -#if SIM_USE_SELECT SimSlirpNetwork *slirp = (SimSlirpNetwork *) opaque; size_t i; +#if SIM_USE_SELECT for (i = 0; i < slirp->lut_alloc; ++i) { if (slirp->lut[i] == INVALID_SOCKET) { slirp->lut[i] = fd; @@ -263,35 +279,69 @@ void register_poll_socket(slirp_os_socket fd, void *opaque) slirp->lut = new_lut; slirp->lut[i] = fd; } - - sim_debug(slirp_dbg_mask(slirp, DBG_SOCKET), slirp->dptr, "register_poll_socket(%s) index %" SIZE_T_FMT "d\n", - print_socket(fd), i); #elif SIM_USE_POLL - /* Not necessary for poll(). */ - (void) opaque; + for (i = 0; i < slirp->fd_idx && have_valid_socket(slirp->fds[i].fd); ++i) + /* NOP */; + + if (i >= slirp->n_fds) { + /* Resize the array... */ + size_t j = slirp->n_fds; + sim_pollfd_t *new_fds; + + slirp->n_fds += FDS_ALLOC_INCR; + new_fds = (sim_pollfd_t *) realloc(slirp->fds, slirp->n_fds * sizeof(sim_pollfd_t)); + ASSURE(new_fds != NULL); + memset(new_fds + j, 0, (slirp->n_fds - j) * sizeof(sim_pollfd_t)); + slirp->fds = new_fds; + } + + slirp->fds[i].fd = fd; + slirp->fds[i].events = slirp->fds[i].revents = 0; + + if (i == slirp->fd_idx) + ++slirp->fd_idx; #endif + + sim_debug(slirp_dbg_mask(slirp, DBG_SOCKET), slirp->dptr, + "register_poll_socket(%" SIM_PRIsocket ") index %" SIZE_T_FMT "u\n", fd, i); } /* Reap a disused socket. */ void unregister_poll_socket(slirp_os_socket fd, void *opaque) { - GLIB_UNUSED_PARAM(opaque); - -#if SIM_USE_SELECT SimSlirpNetwork *slirp = (SimSlirpNetwork *) opaque; size_t i; +#if SIM_USE_SELECT for (i = 0; i < slirp->lut_alloc; ++i) { if (slirp->lut[i] == fd) { slirp->lut[i] = INVALID_SOCKET; sim_debug(slirp_dbg_mask(slirp, DBG_SOCKET), slirp->dptr, - "unregister_poll_socket(%s) index %" SIZE_T_FMT "d\n", print_socket(fd), i); + "unregister_poll_socket(%" SIM_PRIsocket ") index %" SIZE_T_FMT "d\n", fd, i); break; } } + + if (i >= slirp->lut_alloc) { + sim_messagef(SCPE_OK, "unregister_poll_socket(select %" SIM_PRIsocket ") not invalidated.\n", fd); + } #elif SIM_USE_POLL - /* Not necessary for poll(). */ - (void) opaque; + for (i = 0; i < slirp->fd_idx && slirp->fds[i].fd != fd; ++i) + /* NOP */; + + if (i < slirp->fd_idx) { + slirp->fds[i].fd = INVALID_SOCKET; + slirp->fds[i].events = slirp->fds[i].revents = 0; + sim_debug(slirp_dbg_mask(slirp, DBG_SOCKET), slirp->dptr, + "unregister_poll_socket(%" SIM_PRIsocket ") index %" SIZE_T_FMT "d\n", fd, i); + + /* Trim fd_idx? */ + while (!have_valid_socket(slirp->fds[slirp->fd_idx - 1].fd)) { + --slirp->fd_idx; + } + } else { + sim_messagef(SCPE_OK, "unregister_poll_socket(poll %" SIM_PRIsocket ") not invalidated.\n", fd); + } #endif } @@ -308,7 +358,7 @@ static void poll_debugging(uint32_t dbits, DEVICE *dev, const char *prefix, int { SLIRP_POLL_ERR, " SLIRP_POLL_ERR" }, { SLIRP_POLL_HUP, " SLIRP_POLL_HUP" } }; - static const size_t n_translations = sizeof(translations) / sizeof(translations[0]); + const size_t n_translations = sizeof(translations) / sizeof(translations[0]); char *msg = calloc(96 + strlen(prefix) + 1, sizeof(char)); strcpy(msg, prefix); @@ -325,7 +375,6 @@ static void poll_debugging(uint32_t dbits, DEVICE *dev, const char *prefix, int } strcat(msg, "\n"); - /* Don't need to escape "%"-s in msg, since there aren't any. */ sim_debug(dbits, dev, "%s", msg); if (sim_deb != NULL) fflush(sim_deb); @@ -339,9 +388,9 @@ static int add_poll_callback(slirp_os_socket fd, int events, void *opaque) SimSlirpNetwork *slirp = (SimSlirpNetwork *) opaque; int retval = -1; char prefix[128]; + size_t i; #if SIM_USE_SELECT - size_t i; const int event_mask = (SLIRP_POLL_IN | SLIRP_POLL_OUT | SLIRP_POLL_PRI); for (i = 0; i < slirp->lut_alloc; ++i) { @@ -364,50 +413,38 @@ static int add_poll_callback(slirp_os_socket fd, int events, void *opaque) break; } - sprintf(prefix, "add_poll_callback(%s)/select (0x%04x)", print_socket(fd), events & event_mask); + sprintf(prefix, "add_poll_callback(%" SIM_PRIsocket ")/select (0x%04x)", fd, events & event_mask); poll_debugging(slirp_dbg_mask(slirp, DBG_POLL), slirp->dptr, prefix, events & event_mask); } #elif SIM_USE_POLL - if (slirp->fd_idx == slirp->n_fds) { - size_t j = slirp->n_fds; - sim_pollfd_t *new_fds; - - slirp->n_fds += FDS_ALLOC_INCR; - new_fds = (sim_pollfd_t *) realloc(slirp->fds, slirp->n_fds * sizeof(sim_pollfd_t)); - ASSURE(new_fds != NULL); - memset(new_fds + j, 0, (slirp->n_fds - j) * sizeof(sim_pollfd_t)); - slirp->fds = new_fds; - } - - short poll_events = 0; + short poll_events = + ((events & SLIRP_POLL_IN) != 0) * POLLIN + + ((events & SLIRP_POLL_OUT) != 0) * POLLOUT; - if (events & SLIRP_POLL_IN) { - poll_events |= POLLIN; - } - if (events & SLIRP_POLL_OUT) { - poll_events |= POLLOUT; - } -#if !defined(_WIN32) && !defined(_WIN64) +# if !defined(_WIN32) && !defined(_WIN64) /* Not supported on Windows. Unless you like EINVAL. :-) */ - if (events & SLIRP_POLL_PRI) { - poll_events |= POLLPRI; - } - if (events & SLIRP_POLL_ERR) { - poll_events |= POLLERR; - } - if (events & SLIRP_POLL_HUP) { - poll_events |= POLLHUP; + poll_events += + ((events & SLIRP_POLL_PRI) != 0) * POLLPRI + + ((events & SLIRP_POLL_ERR) != 0) * POLLERR + + ((events & SLIRP_POLL_HUP) != 0) * POLLHUP; +# endif + + for (i = 0; i < slirp->fd_idx && slirp->fds[i].fd != fd; ++i) + /* NOP */ ; + + if (i >= slirp->fd_idx) { + sim_messagef(SCPE_IOERR, "add_poll_callback: Unregistered/unknown fd %" SIM_PRIsocket "\n", fd); + return -1; } -#endif sprintf(prefix, "add_poll_callback(%" SIM_PRIsocket ")/poll (0x%04x)", fd, events); poll_debugging(slirp_dbg_mask(slirp, DBG_POLL), slirp->dptr, prefix, events); - slirp->fds[slirp->fd_idx].fd = fd; - slirp->fds[slirp->fd_idx].events = poll_events; - slirp->fds[slirp->fd_idx].revents = 0; + slirp->fds[i].fd = fd; + slirp->fds[i].events = poll_events; + slirp->fds[i].revents = 0; - retval = (int) slirp->fd_idx++; + retval = (int) i; #endif return retval; @@ -435,28 +472,17 @@ static int get_events_callback(int idx, void *opaque) event |= SLIRP_POLL_PRI; } - sprintf(prefix, "get_events_callback(%s)/select (0x%04x)", print_socket(fd), event & event_mask); + sprintf(prefix, "get_events_callback(%" SIM_PRIsocket ")/select (0x%04x)", fd, event & event_mask); poll_debugging(slirp_dbg_mask(slirp, DBG_POLL), slirp->dptr, prefix, event & event_mask); } #elif SIM_USE_POLL if (idx < slirp->fd_idx) { - short poll_events = slirp->fds[idx].revents; - - if (poll_events & POLLIN) { - event |= SLIRP_POLL_IN; - } - if (poll_events & POLLOUT) { - event |= SLIRP_POLL_OUT; - } - if (poll_events & POLLPRI) { - event |= SLIRP_POLL_PRI; - } - if (poll_events & POLLERR) { - event |= SLIRP_POLL_ERR; - } - if (poll_events & POLLHUP) { - event |= SLIRP_POLL_HUP; - } + event = + ((slirp->fds[idx].revents & POLLIN) != 0) * SLIRP_POLL_IN + + ((slirp->fds[idx].revents & POLLOUT) != 0) * SLIRP_POLL_OUT + + ((slirp->fds[idx].revents & POLLPRI) != 0) * SLIRP_POLL_PRI + + ((slirp->fds[idx].revents & POLLERR) != 0) * SLIRP_POLL_ERR + + ((slirp->fds[idx].revents & POLLHUP) != 0) * SLIRP_POLL_HUP; sprintf(prefix, "get_events_callback(%" SIM_PRIsocket ")/poll (0x%04x)", slirp->fds[idx].fd, event); poll_debugging(slirp_dbg_mask(slirp, DBG_POLL), slirp->dptr, prefix, event);