1 /**
2  * @file fg_affinity.c
3  * @brief CPU affinity routines used by Flowgrind
4  */
5 
6 /*
7  * Copyright (C) 2014 Alexander Zimmermann <alexander.zimmermann@netapp.com>
8  *
9  * This file is part of Flowgrind.
10  *
11  * Flowgrind is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation, either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * Flowgrind is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with Flowgrind.  If not, see <http://www.gnu.org/licenses/>.
23  *
24  */
25 
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif /* HAVE_CONFIG_H */
29 
30 #ifdef HAVE_PTHREAD_NP_H
31 #include <pthread_np.h>
32 #endif /* HAVE_PTHREAD_NP */
33 
34 #ifdef HAVE_SYS_CPUSET_H
35 #include <sys/param.h>
36 #include <sys/cpuset.h>
37 #endif /* HAVE_SYS_CPUSET */
38 
39 /* OS X hasn't defined pthread_[set|get]affinity_np */
40 #if !defined(HAVE_PTHREAD_AFFINITY_NP) && defined HAVE_THREAD_POLICY
41 #include <mach/mach.h>
42 #include <mach/thread_policy.h>
43 #endif /* !defined(HAVE_PTHREAD_AFFINITY_NP) && defined HAVE_THREAD_POLICY */
44 
45 #include <stdbool.h>
46 #include <errno.h>
47 #include <unistd.h>
48 
49 #include "fg_affinity.h"
50 
51 #if (!(HAVE_CPU_SET_T) && HAVE_CPUSET_T)
52 /** FreeBSD defines cpuset_t instead of cpu_set_t. Note kFreeBSD defines both. */
53 typedef cpuset_t cpu_set_t;
54 #endif /* HAVE_CPUSET_T */
55 
get_ncores(enum ncore_query query)56 int get_ncores(enum ncore_query query)
57 {
58 	switch (query) {
59 	case NCORE_CONFIG:
60 		/* processors configured */
61 		return (int)sysconf(_SC_NPROCESSORS_CONF);
62 		break;
63 	case NCORE_CURRENT:
64 		/* processors available */
65 		return (int)sysconf(_SC_NPROCESSORS_ONLN);
66 		break;
67 	default:
68 		errno = EINVAL;
69 		return -1;
70 	}
71 }
72 
73 /* Linux and FreeBSD have pthread_[set|get]affinity_np */
74 #if defined HAVE_PTHREAD_AFFINITY_NP
pthread_setaffinity(pthread_t thread,unsigned core)75 int pthread_setaffinity(pthread_t thread, unsigned core)
76 {
77 	cpu_set_t cpuset;
78 	CPU_ZERO(&cpuset);
79 	CPU_SET(core, &cpuset);
80 
81 	int rc = pthread_setaffinity_np(thread, sizeof(cpu_set_t), &cpuset);
82 	return (rc == 0 ? 0 : -1);
83 }
84 
pthread_getaffinity(pthread_t thread,unsigned * core)85 int pthread_getaffinity(pthread_t thread, unsigned *core)
86 {
87 	cpu_set_t cpuset;
88 	int rc = pthread_getaffinity_np(thread, sizeof(cpu_set_t), &cpuset);
89 	if (rc)
90 		return -1;
91 
92 	/* If the cpuset contains only one CPU, then that's the answer. For
93 	 * all other cpuset contents, we treat the binding as unknown */
94 	core = NULL;
95 	bool core_found = false;
96 	for (unsigned i = 0; i < CPU_SETSIZE; i++) {
97 		if (CPU_ISSET(i, &cpuset)) {
98 			if (!core_found) {
99 				core_found = true;
100 				*core = i;
101 			} else {
102 				core_found = false;
103 				core = NULL;
104 				break;
105 			}
106 		}
107 	}
108 
109 	return (core_found ? 0 : -1);
110 }
111 /* OS X hasn't defined pthread_[set|get]affinity_np */
112 #elif defined HAVE_THREAD_POLICY
pthread_setaffinity(pthread_t thread,unsigned core)113 int pthread_setaffinity(pthread_t thread, unsigned core)
114 {
115 	/* Convert pthread ID */
116 	mach_port_t mach_thread = pthread_mach_thread_np(thread);
117 	/* core + 1 to avoid using THREAD_AFFINITY_TAG_NULL */
118 	thread_affinity_policy_data_t policy = { core + 1 };
119 
120 	kern_return_t rc = thread_policy_set(mach_thread,
121 					     THREAD_AFFINITY_POLICY,
122 					     (thread_policy_t) &policy,
123 					     THREAD_AFFINITY_POLICY_COUNT);
124 
125 	return (rc == KERN_SUCCESS ? 0 : -1);
126 }
127 
pthread_getaffinity(pthread_t thread,unsigned * core)128 int pthread_getaffinity(pthread_t thread, unsigned *core)
129 {
130 	/* Convert pthread ID */
131 	mach_port_t mach_thread = pthread_mach_thread_np(thread);
132 	thread_affinity_policy_data_t policy;
133 	mach_msg_type_number_t count = THREAD_AFFINITY_POLICY_COUNT;
134 	boolean_t get_default = FALSE;
135 
136 	kern_return_t rc = thread_policy_get(mach_thread,
137 					     THREAD_AFFINITY_POLICY,
138 					     (thread_policy_t) &policy, &count,
139 					     &get_default);
140 	*core = (unsigned)policy.affinity_tag;
141 
142 	return (rc == KERN_SUCCESS ? 0 : -1);
143 }
144 #endif /* HAVE_PTHREAD_AFFINITY_NP */
145