Home | History | Annotate | Line # | Download | only in isc
      1 /*	$NetBSD: os.c,v 1.6 2026/04/08 00:16:15 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      5  *
      6  * SPDX-License-Identifier: MPL-2.0
      7  *
      8  * This Source Code Form is subject to the terms of the Mozilla Public
      9  * License, v. 2.0. If a copy of the MPL was not distributed with this
     10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
     11  *
     12  * See the COPYRIGHT file distributed with this work for additional
     13  * information regarding copyright ownership.
     14  */
     15 
     16 #include <ctype.h>
     17 #include <inttypes.h>
     18 #include <sys/stat.h>
     19 #include <sys/utsname.h>
     20 
     21 #include <isc/os.h>
     22 #include <isc/string.h>
     23 #include <isc/types.h>
     24 #include <isc/util.h>
     25 
     26 #include "os_p.h"
     27 
     28 static unsigned int isc__os_ncpus = 0;
     29 static unsigned long isc__os_cacheline = ISC_OS_CACHELINE_SIZE;
     30 static mode_t isc__os_umask = 0;
     31 static int kernel_major = -1, kernel_minor = -1, kernel_patch = -1;
     32 static char kernel_name[64];
     33 
     34 #ifdef HAVE_SYSCONF
     35 
     36 #include <unistd.h>
     37 
     38 static long
     39 sysconf_ncpus(void) {
     40 #if defined(_SC_NPROCESSORS_ONLN)
     41 	return sysconf(_SC_NPROCESSORS_ONLN);
     42 #elif defined(_SC_NPROC_ONLN)
     43 	return sysconf(_SC_NPROC_ONLN);
     44 #else  /* if defined(_SC_NPROCESSORS_ONLN) */
     45 	return 0;
     46 #endif /* if defined(_SC_NPROCESSORS_ONLN) */
     47 }
     48 #endif /* HAVE_SYSCONF */
     49 
     50 #if defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_SYSCTLBYNAME)
     51 #include <sys/param.h> /* for NetBSD */
     52 #include <sys/sysctl.h>
     53 #include <sys/types.h> /* for FreeBSD */
     54 
     55 static int
     56 sysctl_ncpus(void) {
     57 	int ncpu, result;
     58 	size_t len;
     59 
     60 	len = sizeof(ncpu);
     61 	result = sysctlbyname("hw.ncpu", &ncpu, &len, 0, 0);
     62 	if (result != -1) {
     63 		return ncpu;
     64 	}
     65 	return 0;
     66 }
     67 #endif /* if defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_SYSCTLBYNAME) */
     68 
     69 #if defined(HAVE_SCHED_GETAFFINITY)
     70 
     71 #if defined(HAVE_SCHED_H)
     72 #include <sched.h>
     73 #endif
     74 
     75 /*
     76  * Administrators may wish to constrain the set of cores that BIND runs
     77  * on via the 'taskset' or 'numactl' programs (or equivalent on other
     78  * O/S), for example to achieve higher (or more stable) performance by
     79  * more closely associating threads with individual NIC rx queues. If
     80  * the admin has used taskset, it follows that BIND ought to
     81  * automatically use the given number of CPUs rather than the system
     82  * wide count.
     83  */
     84 static int
     85 sched_affinity_ncpus(void) {
     86 	cpu_set_t cpus;
     87 	int result;
     88 
     89 	result = sched_getaffinity(0, sizeof(cpus), &cpus);
     90 	if (result != -1) {
     91 #ifdef CPU_COUNT
     92 		return CPU_COUNT(&cpus);
     93 #else
     94 		int i, n = 0;
     95 
     96 		for (i = 0; i < CPU_SETSIZE; ++i) {
     97 			if (CPU_ISSET(i, &cpus)) {
     98 				++n;
     99 			}
    100 		}
    101 		return n;
    102 #endif
    103 	}
    104 	return 0;
    105 }
    106 #endif
    107 
    108 /*
    109  * Affinity detecting variant of sched_affinity_cpus() for FreeBSD
    110  */
    111 
    112 #if defined(HAVE_SYS_CPUSET_H) && defined(HAVE_CPUSET_GETAFFINITY)
    113 #include <sys/cpuset.h>
    114 #include <sys/param.h>
    115 
    116 static int
    117 cpuset_affinity_ncpus(void) {
    118 	cpuset_t cpus;
    119 	int result;
    120 
    121 	result = cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1,
    122 				    sizeof(cpus), &cpus);
    123 	if (result != -1) {
    124 		int i, n = 0;
    125 		for (i = 0; i < CPU_SETSIZE; ++i) {
    126 			if (CPU_ISSET(i, &cpus)) {
    127 				++n;
    128 			}
    129 		}
    130 		return n;
    131 	}
    132 	return 0;
    133 }
    134 #endif
    135 
    136 static void
    137 ncpus_initialize(void) {
    138 #if defined(HAVE_SYS_CPUSET_H) && defined(HAVE_CPUSET_GETAFFINITY)
    139 	if (isc__os_ncpus <= 0) {
    140 		isc__os_ncpus = cpuset_affinity_ncpus();
    141 	}
    142 #endif
    143 #if defined(HAVE_SCHED_GETAFFINITY)
    144 	if (isc__os_ncpus <= 0) {
    145 		isc__os_ncpus = sched_affinity_ncpus();
    146 	}
    147 #endif
    148 #if defined(HAVE_SYSCONF)
    149 	if (isc__os_ncpus <= 0) {
    150 		isc__os_ncpus = sysconf_ncpus();
    151 	}
    152 #endif /* if defined(HAVE_SYSCONF) */
    153 #if defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_SYSCTLBYNAME)
    154 	if (isc__os_ncpus <= 0) {
    155 		isc__os_ncpus = sysctl_ncpus();
    156 	}
    157 #endif /* if defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_SYSCTLBYNAME) */
    158 	if (isc__os_ncpus <= 0) {
    159 		isc__os_ncpus = 1;
    160 	}
    161 }
    162 
    163 static void
    164 umask_initialize(void) {
    165 	isc__os_umask = umask(0);
    166 	(void)umask(isc__os_umask);
    167 }
    168 
    169 static void
    170 kernel_initialize(void) {
    171 	struct utsname buffer;
    172 
    173 	if (uname(&buffer) == -1) {
    174 		return;
    175 	}
    176 
    177 	(void)sscanf(buffer.release, "%d.%d.%d", &kernel_major, &kernel_minor,
    178 		     &kernel_patch);
    179 	(void)strlcpy(kernel_name, buffer.sysname, sizeof(kernel_name));
    180 }
    181 
    182 unsigned int
    183 isc_os_ncpus(void) {
    184 	return isc__os_ncpus;
    185 }
    186 
    187 unsigned long
    188 isc_os_cacheline(void) {
    189 	return isc__os_cacheline;
    190 }
    191 
    192 mode_t
    193 isc_os_umask(void) {
    194 	return isc__os_umask;
    195 }
    196 
    197 void
    198 isc_os_kernel(char **name, int *major, int *minor, int *patch) {
    199 	SET_IF_NOT_NULL(name, kernel_name)
    200 	SET_IF_NOT_NULL(major, kernel_major);
    201 	SET_IF_NOT_NULL(minor, kernel_minor);
    202 	SET_IF_NOT_NULL(patch, kernel_patch);
    203 }
    204 
    205 void
    206 isc__os_initialize(void) {
    207 	umask_initialize();
    208 	ncpus_initialize();
    209 	kernel_initialize();
    210 #if defined(HAVE_SYSCONF) && defined(_SC_LEVEL1_DCACHE_LINESIZE)
    211 	long s = sysconf(_SC_LEVEL1_DCACHE_LINESIZE);
    212 	if (s > 0 && (unsigned long)s > isc__os_cacheline) {
    213 		isc__os_cacheline = s;
    214 	}
    215 #endif
    216 }
    217 
    218 void
    219 isc__os_shutdown(void) {
    220 	/* empty, but defined for completeness */;
    221 }
    222