/* * Copyright (c) 2018 Amazon.com, Inc. or its affiliates. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * BSD license below: * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * - Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif /* _GNU_SOURCE */ #include #include #include #include "ofi.h" #include "ofi_osd.h" #include #include #include #include #include #include static size_t ofi_base_page_size; static unsigned long ofi_smaps_page_size(FILE *file) { int n; unsigned long size = ofi_base_page_size; char buf[1024]; while (fgets(buf, sizeof(buf), file) != NULL) { if (!strstr(buf, "KernelPageSize:")) continue; n = sscanf(buf, "%*s %lu", &size); if (n < 1) continue; /* page size is printed in Kb */ size = size * 1024; break; } return size; } ssize_t ofi_get_addr_page_size(const void *addr) { pid_t pid; ssize_t ret; FILE *file; char buf[1024]; pid = getpid(); snprintf(buf, sizeof(buf), "/proc/%d/smaps", pid); if (!ofi_base_page_size) { ret = ofi_sysconf(_SC_PAGESIZE); if (ret < 0) { FI_WARN(&core_prov, FI_LOG_CORE, "ofi_sysconf(_SC_PAGESIZE) failed: %d:%s\n", -errno, strerror(errno)); return -FI_EINVAL; } ofi_base_page_size = ret; } file = fopen(buf, "re"); if (!file) return ofi_base_page_size; ret = ofi_base_page_size; while (fgets(buf, sizeof(buf), file) != NULL) { int n; uintptr_t range_start, range_end; n = sscanf(buf, "%" SCNxPTR "-%" SCNxPTR, &range_start, &range_end); if (n < 2) continue; if ((uintptr_t) addr >= range_start && (uintptr_t) addr < range_end) { ret = ofi_smaps_page_size(file); break; } } fclose(file); return ret; } ssize_t ofi_get_hugepage_size(void) { FILE *fd; char *line = NULL; size_t len = 0; ssize_t val = -1; fd = fopen("/proc/meminfo", "r"); if (!fd) return -errno; while (getline(&line, &len, fd) != -1) if (sscanf(line, "Hugepagesize: %zi kB", &val) == 1) break; free(line); fclose(fd); if (val == -1) return -FI_ENOENT; return val * 1024; } #ifdef HAVE_ETHTOOL #if HAVE_DECL_ETHTOOL_CMD_SPEED static inline uint32_t ofi_ethtool_cmd_speed(struct ethtool_cmd *ecmd) { return ethtool_cmd_speed(ecmd); } #else /* HAVE_DECL_ETHTOOL_CMD_SPEED */ static inline uint32_t ofi_ethtool_cmd_speed(struct ethtool_cmd *ecmd) { return ecmd->speed; } #endif /* HAVE_DECL_ETHTOOL_CMD_SPEED */ #if HAVE_DECL_SPEED_UNKNOWN static inline int ofi_ethtool_is_known(uint32_t speed_mbps) { return (speed_mbps != SPEED_UNKNOWN); } #else /* HAVE_DECL_SPEED_UNKNOWN */ static inline int ofi_ethtool_is_known(uint32_t speed_mbps) { return ((speed_mbps != 0) && ((uint16_t)speed_mbps != (uint16_t)-1)); } #endif /* HAVE_DECL_SPEED_UNKNOWN */ /* Note: If the value of the speed is unknown, set it to 100 Mb/s */ static inline size_t ofi_ethtool_get_speed(struct ethtool_cmd *ecmd) { uint32_t speed_mbps = ofi_ethtool_cmd_speed(ecmd); return (ofi_ethtool_is_known(speed_mbps) ? speed_mbps : 100); } size_t ofi_ifaddr_get_speed(struct ifaddrs *ifa) { struct ethtool_cmd cmd = { .cmd = ETHTOOL_GSET, }; struct ifreq ifr = { .ifr_data = (void *)&cmd, }; SOCKET fd; int ret; fd = socket(ifa->ifa_addr->sa_family, SOCK_STREAM, IPPROTO_IP); if (fd < 0) return 0; strncpy(ifr.ifr_name, ifa->ifa_name, sizeof(ifr.ifr_name) - 1); ret = ioctl(fd, SIOCETHTOOL, &ifr); if (ret) { close(fd); return 0; } close(fd); return ofi_ethtool_get_speed(&cmd); } #else /* HAVE_ETHTOOL */ size_t ofi_ifaddr_get_speed(struct ifaddrs *ifa) { FILE *fd; char *line = NULL; size_t len = 0; char *speed_filename_prefix = "/sys/class/net/"; char *speed_filename_suffix = "/speed"; char *speed_filename; size_t speed; /* IF_NAMESIZE includes NULL-terminated symbol */ size_t filename_len = strlen(speed_filename_prefix) + strlen(speed_filename_prefix) + IF_NAMESIZE; speed_filename = calloc(1, filename_len); if (!speed_filename) return 0; snprintf(speed_filename, filename_len, "%s%s%s", speed_filename_prefix, ifa->ifa_name, speed_filename_suffix); fd = fopen(speed_filename, "r"); if (!fd) goto err1; if (getline(&line, &len, fd) == -1) { goto err2; } if (sscanf(line, "%zu", &speed) != 1) goto err3; free(line); fclose(fd); free(speed_filename); return speed; err3: free(line); err2: fclose(fd); err1: free(speed_filename); return 0; } #endif /* HAVE_ETHTOOL */