assert.h \
compat.h \
compat_shared.h \
+ cpu.h \
debug.h \
drand48.h \
eui64_aton.h \
--- /dev/null
+/*
+ * $Id$
+ */
+
+#ifndef SQUID_CONFIG_H
+#include "config.h"
+#endif
+
+#ifndef SQUID_COMPAT_CPU_H
+#define SQUID_COMPAT_CPU_H
+
+#if HAVE_CPU_AFFINITY
+
+#if HAVE_SCHED_H
+#include <sched.h>
+#endif
+
+// glibc prior to 2.6 lacks CPU_COUNT
+#ifndef CPU_COUNT
+#define CPU_COUNT(set) CpuCount(set)
+/// CPU_COUNT replacement
+inline int
+CpuCount(const cpu_set_t *set)
+{
+ int count = 0;
+ for (int i = 0; i < CPU_SETSIZE; ++i) {
+ if (CPU_ISSET(i, set))
+ ++count;
+ }
+ return count;
+}
+#endif /* CPU_COUNT */
+
+// glibc prior to 2.7 lacks CPU_AND
+#ifndef CPU_AND
+#define CPU_AND(destset, srcset1, srcset2) CpuAnd((destset), (srcset1), (srcset2))
+/// CPU_AND replacement
+inline void
+CpuAnd(cpu_set_t *destset, const cpu_set_t *srcset1, const cpu_set_t *srcset2)
+{
+ CPU_ZERO(destset);
+ for (int i = 0; i < CPU_SETSIZE; ++i) {
+ if (CPU_ISSET(i, srcset1) && CPU_ISSET(i, srcset2))
+ CPU_SET(i, destset);
+ }
+}
+#endif /* CPU_AND */
+
+#else /* HAVE_CPU_AFFINITY */
+
+#if HAVE_ERRNO_H
+#include <errno.h> /* for ENOTSUP */
+#endif
+
+/* failing replacements to minimize the number of if-HAVE_CPU_AFFINITYs */
+typedef struct
+{
+ int bits;
+} cpu_set_t;
+#define CPU_SETSIZE 0
+#define CPU_COUNT(set) 0
+#define CPU_AND(destset, srcset1, srcset2) (void)0
+#define CPU_ZERO(set) (void)0
+inline int sched_setaffinity(int, size_t, cpu_set_t *) { return ENOTSUP; }
+inline int sched_getaffinity(int, size_t, cpu_set_t *) { return ENOTSUP; }
+
+#endif /* HAVE_CPU_AFFINITY */
+
+#endif /* SQUID_COMPAT_CPU_H */
__res_init \
rint \
sbrk \
+ sched_getaffinity \
+ sched_setaffinity \
select \
seteuid \
setgroups \
select) AC_DEFINE(USE_SELECT,1,[Use select() for the IO loop]) ;;
esac
+if test "x$ac_cv_func_sched_getaffinity" = "xyes" -a "x$ac_cv_func_sched_setaffinity" = "xyes" ; then
+ AC_DEFINE(HAVE_CPU_AFFINITY,1,[Support setting CPU affinity for workers])
+fi
+
SQUID_CHECK_SETRESUID_WORKS
if test "x$squid_cv_resuid_works" = "xyes" ; then
AC_DEFINE(HAVE_SETRESUID,1,[Yay! Another Linux brokenness. Knowing that setresuid() exists is not enough, because RedHat 5.0 declares setresuid() but does not implement it.])
--- /dev/null
+/*
+ * $Id$
+ *
+ * DEBUG: section 54 Interprocess Communication
+ *
+ */
+
+#include "config.h"
+#include "base/TextException.h"
+#include "CpuAffinity.h"
+#include "CpuAffinityMap.h"
+#include "CpuAffinitySet.h"
+#include "structs.h"
+
+#include <algorithm>
+
+static CpuAffinitySet *TheCpuAffinitySet = NULL;
+
+
+void
+CpuAffinityInit()
+{
+ Must(!TheCpuAffinitySet);
+ if (Config.cpuAffinityMap) {
+ const int processNumber = InDaemonMode() ? KidIdentifier : 1;
+ TheCpuAffinitySet = Config.cpuAffinityMap->calculateSet(processNumber);
+ if (TheCpuAffinitySet)
+ TheCpuAffinitySet->apply();
+ }
+}
+
+void
+CpuAffinityReconfigure()
+{
+ if (TheCpuAffinitySet) {
+ TheCpuAffinitySet->undo();
+ delete TheCpuAffinitySet;
+ TheCpuAffinitySet = NULL;
+ }
+ CpuAffinityInit();
+}
+
+void
+CpuAffinityCheck()
+{
+ if (Config.cpuAffinityMap) {
+ Must(!Config.cpuAffinityMap->processes().empty());
+ const int maxProcess =
+ *std::max_element(Config.cpuAffinityMap->processes().begin(),
+ Config.cpuAffinityMap->processes().end());
+
+ // in no-deamon mode, there is one process regardless of squid.conf
+ const int numberOfProcesses = InDaemonMode() ? NumberOfKids() : 1;
+
+ if (maxProcess > numberOfProcesses) {
+ debugs(54, DBG_IMPORTANT, "WARNING: 'cpu_affinity_map' has "
+ "non-existing process number(s)");
+ }
+ }
+}
--- /dev/null
+/*
+ * $Id$
+ *
+ */
+
+#ifndef SQUID_CPU_AFFINITY_H
+#define SQUID_CPU_AFFINITY_H
+
+#include "config.h"
+
+/// set CPU affinity for this process on startup
+SQUIDCEXTERN void CpuAffinityInit();
+
+/// reconfigure CPU affinity for this process
+SQUIDCEXTERN void CpuAffinityReconfigure();
+
+/// check CPU affinity configuration and print warnings if needed
+SQUIDCEXTERN void CpuAffinityCheck();
+
+
+#endif // SQUID_CPU_AFFINITY_H
--- /dev/null
+/*
+ * $Id$
+ *
+ * DEBUG: section 54 Interprocess Communication
+ *
+ */
+
+#include "config.h"
+#include "base/TextException.h"
+#include "CpuAffinityMap.h"
+#include "CpuAffinitySet.h"
+#include "Debug.h"
+
+
+bool
+CpuAffinityMap::add(const Vector<int> &aProcesses, const Vector<int> &aCores)
+{
+ if (aProcesses.size() != aCores.size())
+ return false;
+
+ for (size_t i = 0; i < aProcesses.size(); ++i) {
+ const int process = aProcesses[i];
+ const int core = aCores[i];
+ if (process <= 0 || core <= 0)
+ return false;
+ theProcesses.push_back(process);
+ theCores.push_back(core);
+ }
+
+ return true;
+}
+
+CpuAffinitySet *
+CpuAffinityMap::calculateSet(const int targetProcess) const
+{
+ Must(theProcesses.size() == theCores.size());
+ int core = 0;
+ for (size_t i = 0; i < theProcesses.size(); ++i) {
+ const int process = theProcesses[i];
+ if (process == targetProcess)
+ {
+ if (core > 0) {
+ debugs(54, DBG_CRITICAL, "WARNING: conflicting "
+ "'cpu_affinity_map' for process number " << process <<
+ ", using the last core seen: " << theCores[i]);
+ }
+ core = theCores[i];
+ }
+ }
+ CpuAffinitySet *cpuAffinitySet = NULL;
+ if (core > 0) {
+ cpuAffinitySet = new CpuAffinitySet;
+ cpu_set_t cpuSet;
+ CPU_ZERO(&cpuSet);
+ CPU_SET(core - 1, &cpuSet);
+ cpuAffinitySet->set(cpuSet);
+ }
+ return cpuAffinitySet;
+}
--- /dev/null
+/*
+ * $Id$
+ *
+ */
+
+#ifndef SQUID_CPU_AFFINITY_MAP_H
+#define SQUID_CPU_AFFINITY_MAP_H
+
+#include "Array.h"
+
+class CpuAffinitySet;
+
+
+/// stores cpu_affinity_map configuration
+class CpuAffinityMap
+{
+public:
+ /// append cpu_affinity_map option
+ bool add(const Vector<int> &aProcesses, const Vector<int> &aCores);
+
+ /// calculate CPU set for this process
+ CpuAffinitySet *calculateSet(const int targetProcess) const;
+
+ /// returns list of process numbers
+ const Vector<int> &processes() const { return theProcesses; }
+
+ /// returns list of cores
+ const Vector<int> &cores() const { return theCores; }
+
+private:
+ Vector<int> theProcesses; ///< list of process numbers
+ Vector<int> theCores; ///< list of cores
+};
+
+#endif // SQUID_CPU_AFFINITY_MAP_H
--- /dev/null
+/*
+ * $Id$
+ *
+ * DEBUG: section 54 Interprocess Communication
+ *
+ */
+
+#include "config.h"
+#include "base/TextException.h"
+#include "CpuAffinitySet.h"
+#include "Debug.h"
+#include "util.h"
+
+#if HAVE_STRING_H
+#include <string.h> /* for memcpy() */
+#endif
+
+
+CpuAffinitySet::CpuAffinitySet()
+{
+ CPU_ZERO(&theCpuSet);
+ CPU_ZERO(&theOrigCpuSet);
+}
+
+void
+CpuAffinitySet::apply()
+{
+ Must(CPU_COUNT(&theCpuSet) > 0); // CPU affinity mask set
+ Must(!applied());
+
+ bool success = false;
+ if (sched_getaffinity(0, sizeof(theOrigCpuSet), &theOrigCpuSet)) {
+ debugs(54, DBG_IMPORTANT, "ERROR: failed to get CPU affinity for "
+ "process PID " << getpid() << ", ignoring CPU affinity for "
+ "this process: " << xstrerror());
+ } else {
+ cpu_set_t cpuSet;
+ xmemcpy(&cpuSet, &theCpuSet, sizeof(cpuSet));
+ CPU_AND(&cpuSet, &cpuSet, &theOrigCpuSet);
+ if (CPU_COUNT(&cpuSet) <= 0) {
+ debugs(54, DBG_IMPORTANT, "ERROR: invalid CPU affinity for process "
+ "PID " << getpid() << ", may be caused by an invalid core in "
+ "'cpu_affinity_map' or by external affinity restrictions");
+ } else if (sched_setaffinity(0, sizeof(cpuSet), &cpuSet)) {
+ debugs(54, DBG_IMPORTANT, "ERROR: failed to set CPU affinity for "
+ "process PID " << getpid() << ": " << xstrerror());
+ } else
+ success = true;
+ }
+ if (!success)
+ CPU_ZERO(&theOrigCpuSet);
+}
+
+void
+CpuAffinitySet::undo()
+{
+ if (applied()) {
+ if (sched_setaffinity(0, sizeof(theOrigCpuSet), &theOrigCpuSet)) {
+ debugs(54, DBG_IMPORTANT, "ERROR: failed to restore original CPU "
+ "affinity for process PID " << getpid() << ": " <<
+ xstrerror());
+ }
+ CPU_ZERO(&theOrigCpuSet);
+ }
+}
+
+bool
+CpuAffinitySet::applied() const
+{
+ return (CPU_COUNT(&theOrigCpuSet) > 0);
+}
+
+void
+CpuAffinitySet::set(const cpu_set_t &aCpuSet)
+{
+ xmemcpy(&theCpuSet, &aCpuSet, sizeof(theCpuSet));
+}
--- /dev/null
+/*
+ * $Id$
+ *
+ */
+
+#ifndef SQUID_CPU_AFFINITY_SET_H
+#define SQUID_CPU_AFFINITY_SET_H
+
+#include "config.h"
+#include "compat/cpu.h"
+
+/// cpu affinity management for a single process
+class CpuAffinitySet
+{
+public:
+ CpuAffinitySet();
+
+ /// set CPU affinity for this process
+ void apply();
+
+ /// undo CPU affinity changes for this process
+ void undo();
+
+ /// whether apply() was called and was not undone
+ bool applied() const;
+
+ /// set CPU affinity mask
+ void set(const cpu_set_t &aCpuSet);
+
+private:
+ cpu_set_t theCpuSet; ///< configured CPU affinity for this process
+ cpu_set_t theOrigCpuSet; ///< CPU affinity for this process before apply()
+};
+
+#endif // SQUID_CPU_AFFINITY_SET_H
ConfigParser.cc \
ConfigParser.h \
ConnectionDetail.h \
+ CpuAffinity.cc \
+ CpuAffinity.h \
+ CpuAffinityMap.cc \
+ CpuAffinityMap.h \
+ CpuAffinitySet.cc \
+ CpuAffinitySet.h \
debug.cc \
Debug.h \
defines.h \
$(squid_COMMSOURCES) \
ConfigOption.cc \
ConfigParser.cc \
+ CpuAffinityMap.cc \
+ CpuAffinityMap.h \
+ CpuAffinitySet.cc \
+ CpuAffinitySet.h \
$(DELAY_POOL_SOURCE) \
disk.cc \
dlink.h \
$(squid_COMMSOURCES) \
ConfigOption.cc \
ConfigParser.cc \
+ CpuAffinityMap.cc \
+ CpuAffinityMap.h \
+ CpuAffinitySet.cc \
+ CpuAffinitySet.h \
$(DELAY_POOL_SOURCE) \
disk.cc \
dlink.h \
$(squid_COMMSOURCES) \
ConfigOption.cc \
ConfigParser.cc \
+ CpuAffinityMap.cc \
+ CpuAffinityMap.h \
+ CpuAffinitySet.cc \
+ CpuAffinitySet.h \
$(DELAY_POOL_SOURCE) \
disk.cc \
dlink.h \
$(squid_COMMSOURCES) \
ConfigOption.cc \
ConfigParser.cc \
+ CpuAffinityMap.cc \
+ CpuAffinityMap.h \
+ CpuAffinitySet.cc \
+ CpuAffinitySet.h \
tests/stub_main_cc.cc \
debug.cc \
$(DELAY_POOL_SOURCE) \
$(squid_COMMSOURCES) \
ConfigOption.cc \
ConfigParser.cc \
+ CpuAffinityMap.cc \
+ CpuAffinityMap.h \
+ CpuAffinitySet.cc \
+ CpuAffinitySet.h \
$(DELAY_POOL_SOURCE) \
disk.cc \
dlink.h \
$(squid_COMMSOURCES) \
ConfigOption.cc \
ConfigParser.cc \
+ CpuAffinityMap.cc \
+ CpuAffinityMap.h \
+ CpuAffinitySet.cc \
+ CpuAffinitySet.h \
$(DELAY_POOL_SOURCE) \
disk.cc \
dlink.h \
#include "auth/Scheme.h"
#include "CacheManager.h"
#include "ConfigParser.h"
+#include "CpuAffinityMap.h"
#include "eui/Config.h"
#if USE_SQUID_ESI
#include "esi/Parser.h"
static void parse_b_size_t(size_t * var);
static void parse_b_int64_t(int64_t * var);
+static bool parseNamedIntList(const char *data, const String &name, Vector<int> &list);
+
+static void parse_CpuAffinityMap(CpuAffinityMap **const cpuAffinityMap);
+static void dump_CpuAffinityMap(StoreEntry *const entry, const char *const name, const CpuAffinityMap *const cpuAffinityMap);
+static void free_CpuAffinityMap(CpuAffinityMap **const cpuAffinityMap);
+
static int parseOneConfigFile(const char *file_name, unsigned int depth);
/*
}
}
+/// parses list of integers form name=N1,N2,N3,...
+static bool
+parseNamedIntList(const char *data, const String &name, Vector<int> &list)
+{
+ if (data && (strncmp(data, name.rawBuf(), name.size()) == 0)) {
+ data += name.size();
+ if (*data == '=') {
+ while (true) {
+ ++data;
+ int value = 0;
+ if (!StringToInt(data, value, &data, 10))
+ break;
+ list.push_back(value);
+ if (*data == '\0' || *data != ',')
+ break;
+ }
+ }
+ }
+ return *data == '\0';
+}
+
+static void
+parse_CpuAffinityMap(CpuAffinityMap **const cpuAffinityMap) {
+#if !HAVE_CPU_AFFINITY
+ debugs(3, DBG_CRITICAL, "FATAL: Squid built with no CPU affinity " <<
+ "support, do not set 'cpu_affinity_map'");
+ self_destruct();
+#endif /* HAVE_CPU_AFFINITY */
+
+ if (!*cpuAffinityMap)
+ *cpuAffinityMap = new CpuAffinityMap;
+
+ const char *const pToken = strtok(NULL, w_space);
+ const char *const cToken = strtok(NULL, w_space);
+ Vector<int> processes, cores;
+ if (!parseNamedIntList(pToken, "process_numbers", processes)) {
+ debugs(3, DBG_CRITICAL, "FATAL: bad 'process_numbers' parameter " <<
+ "in 'cpu_affinity_map'");
+ self_destruct();
+ } else if (!parseNamedIntList(cToken, "cores", cores)) {
+ debugs(3, DBG_CRITICAL, "FATAL: bad 'cores' parameter in " <<
+ "'cpu_affinity_map'");
+ self_destruct();
+ } else if (!(*cpuAffinityMap)->add(processes, cores)) {
+ debugs(3, DBG_CRITICAL, "FATAL: bad 'cpu_affinity_map'; " <<
+ "process_numbers and cores lists differ in length or " <<
+ "contain numbers <= 0");
+ self_destruct();
+ }
+}
+
+static void
+dump_CpuAffinityMap(StoreEntry *const entry, const char *const name, const CpuAffinityMap *const cpuAffinityMap) {
+ if (cpuAffinityMap) {
+ storeAppendPrintf(entry, "%s process_numbers=", name);
+ for (size_t i = 0; i < cpuAffinityMap->processes().size(); ++i) {
+ storeAppendPrintf(entry, "%s%i", (i ? "," : ""),
+ cpuAffinityMap->processes()[i]);
+ }
+ storeAppendPrintf(entry, " cores=");
+ for (size_t i = 0; i < cpuAffinityMap->processes().size(); ++i) {
+ storeAppendPrintf(entry, "%s%i", (i ? "," : ""),
+ cpuAffinityMap->cores()[i]);
+ }
+ storeAppendPrintf(entry, "\n");
+ }
+}
+
+static void
+free_CpuAffinityMap(CpuAffinityMap **const cpuAffinityMap) {
+ delete *cpuAffinityMap;
+ *cpuAffinityMap = NULL;
+}
+
#if USE_ADAPTATION
static void
b_size_t
cachedir cache_replacement_policy
cachemgrpasswd
+CpuAffinityMap
debug
delay_pool_access acl delay_class
delay_pool_class delay_pools
does (e.g., listen on http_port and forward HTTP requests).
DOC_END
+NAME: cpu_affinity_map
+TYPE: CpuAffinityMap
+LOC: Config.cpuAffinityMap
+DEFAULT: none
+DOC_START
+ Usage: cpu_affinity_map process_numbers=P1,P2,... cores=C1,C2,...
+
+ Sets 1:1 mapping between Squid processes and CPU cores. For example,
+
+ cpu_affinity_map process_numbers=1,2,3,4 cores=1,3,5,7
+
+ affects processes 1 through 4 only and places them on the first
+ four even cores, starting with core #1.
+
+ CPU cores are numbered starting from 1. Requires support for
+ sched_getaffinity(2) and sched_setaffinity(2) system calls.
+
+ Multiple cpu_affinity_map options are merged.
+
+ See also: workers
+DOC_END
+
EOF
#include "auth/Gadgets.h"
#include "base/TextException.h"
#include "ConfigParser.h"
+#include "CpuAffinity.h"
#include "errorpage.h"
#include "event.h"
#include "EventLoop.h"
Config.workers = oldWorkers;
}
+ if (IamPrimaryProcess())
+ CpuAffinityCheck();
+ CpuAffinityReconfigure();
+
setUmask(Config.umask);
Mem::Report();
setEffectiveUser();
return 0;
}
+ if (IamPrimaryProcess())
+ CpuAffinityCheck();
+ CpuAffinityInit();
+
if (!opt_no_daemon && Config.workers > 0)
watch_child(argv);
SQUIDCEXTERN bool IamCoordinatorProcess();
/// whether the current process handles HTTP transactions and such
SQUIDCEXTERN bool IamWorkerProcess();
+/// Whether we are running in daemon mode
+SQUIDCEXTERN bool InDaemonMode(); // try using specific Iam*() checks above first
/// Whether there should be more than one worker process running
SQUIDCEXTERN bool UsingSmp(); // try using specific Iam*() checks above first
+/// number of Kid processes as defined in src/ipc/Kid.h
+SQUIDCEXTERN int NumberOfKids();
SQUIDCEXTERN int DebugSignal;
/* AYJ debugs function to show locations being reset with memset() */
/* forward decl for SquidConfig, see RemovalPolicy.h */
+class CpuAffinityMap;
class RemovalPolicySettings;
class external_acl;
class Store;
int umask;
int max_filedescriptors;
int workers;
+ CpuAffinityMap *cpuAffinityMap;
#if USE_LOADABLE_MODULES
wordlist *loadable_module_names;
return 0 < KidIdentifier && KidIdentifier <= Config.workers;
}
+bool
+InDaemonMode()
+{
+ return !opt_no_daemon && Config.workers > 0;
+}
+
bool
UsingSmp()
{
return IamCoordinatorProcess();
}
+int
+NumberOfKids()
+{
+ // no kids in no-daemon mode
+ if (!InDaemonMode())
+ return 0;
+
+ // workers + the coordinator process
+ if (UsingSmp())
+ return Config.workers + 1;
+
+ return Config.workers;
+}
+
void
writePidFile(void)
{