Add cpu_affinity_map configuration option to bind workers to CPU cores.
Allows the admin to place all workers on individual cores without
writing a lot of if-statements. For example,
cpu_affinity_map process_numbers=1,2,3,4 cores=1,3,5,7
will have an effect for kids 1 through 4 only and will place them on
even cores starting with core #1.
If there are conflicts for a given process, the latest option wins and
a warning is printed. If the number of specified processes do not match
the number of specified cores, Squid quits with an error. Multiple
cpu_affinity_map options are merged.
Squid builds on systems without Linux CPU affinity calls, but setting
affinity only works if there are sched_getaffinity(2) and
sched_setaffinity(2) available. If there is no OS support but
cpu_affinity options are configured, Squid quits with an error. If there
is OS support but calls fail, Squid prints an error but does not quit.
Add cpu_affinity_map configuration option to bind workers to CPU cores.
Allows the admin to place all workers on individual cores without writing a
lot of if-statements. For example,
cpu_affinity_map process_numbers=1,2,3,4 cores=1,3,5,7
will have an effect for kids 1 through 4 only and will place them on even
cores starting with core #1.
If there are conflicts for a given process, the latest option wins and a
warning is printed. If the number of specified processes do not match the
number of specified cores, Squid quits with an error. Multiple
cpu_affinity_map options are merged.
Squid builds on systems without Linux CPU affinity calls, but setting affinity
only works if there are sched_getaffinity(2) and sched_setaffinity(2)
available. If there is no OS support but cpu_affinity options are configured,
Squid quits with an error. If there is OS support but calls fail, Squid prints
an error but does not quit.
=== modified file 'configure.in'
--- configure.in 2010-09-10 15:41:41 +0000
+++ configure.in 2010-09-10 23:57:42 +0000
@@ -2917,40 +2917,42 @@ AC_CHECK_FUNCS(\
memmove \
memset \
mkstemp \
mktime \
mstats \
poll \
prctl \
pthread_attr_setschedparam \
pthread_attr_setscope \
pthread_setschedparam \
pthread_sigmask \
putenv \
random \
regcomp \
regexec \
regfree \
res_init \
__res_init \
rint \
sbrk \
+ sched_getaffinity \
+ sched_setaffinity \
select \
seteuid \
setgroups \
setpgrp \
setrlimit \
setsid \
sigaction \
snprintf \
socketpair \
srand48 \
srandom \
statfs \
sysconf \
syslog \
timegm \
vsnprintf \
)
dnl ... and some we provide local replacements for
AC_REPLACE_FUNCS(\
drand48 \
@@ -2991,40 +2993,44 @@ else
fi
AC_MSG_NOTICE([Using ${squid_opt_io_loop_engine} for the IO loop.])
AM_CONDITIONAL([USE_POLL], [test $squid_opt_io_loop_engine = poll])
AM_CONDITIONAL([USE_EPOLL], [test $squid_opt_io_loop_engine = epoll])
AM_CONDITIONAL([USE_SELECT], [test $squid_opt_io_loop_engine = select])
AM_CONDITIONAL([USE_SELECT_SIMPLE], [test $squid_opt_io_loop_engine = select_simple])
AM_CONDITIONAL([USE_SELECT_WIN32], [test $squid_opt_io_loop_engine = select_win32])
AM_CONDITIONAL([USE_KQUEUE], [test $squid_opt_io_loop_engine = kqueue])
AM_CONDITIONAL([USE_DEVPOLL], [test $squid_opt_io_loop_engine = devpoll])
case $squid_opt_io_loop_engine in
epoll) AC_DEFINE(USE_EPOLL,1,[Use epoll() for the IO loop]) ;;
poll) AC_DEFINE(USE_POLL,1,[Use poll() for the IO loop]) ;;
kqueue) AC_DEFINE(USE_KQUEUE,1,[Use kqueue() for the IO loop]) ;;
select_win32) AC_DEFINE(USE_SELECT_WIN32,1,[Use Winsock select() for the IO loop]) ;;
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.])
fi
SQUID_CHECK_FUNC_STRNSTR
SQUID_CHECK_FUNC_VACOPY
SQUID_CHECK_FUNC___VACOPY
dnl IP-Filter support requires ipf header files. These aren't
dnl installed by default, so we need to check for them
if test "x$enable_ipf_transparent" != "xno" ; then
AC_MSG_CHECKING(for availability of IP-Filter header files)
# hold on to your hats...
if test "x$ac_cv_header_ip_compat_h" = "xyes" -o \
"x$ac_cv_header_ip_fil_compat_h" = "xyes" -o \
"x$ac_cv_header_netinet_ip_compat_h" = "xyes" -o \
"x$ac_cv_header_netinet_ip_fil_compat_h" = "xyes" ; then
have_ipfilter_compat_header="yes"
=== added file 'src/CpuAffinity.cc'
--- src/CpuAffinity.cc 1970-01-01 00:00:00 +0000
+++ src/CpuAffinity.cc 2010-09-11 05:24:09 +0000
@@ -0,0 +1,57 @@
+/*
+ * $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());
+ const int numberOfProcesses = InDaemonMode() ? NumberOfKids() : 1;
+ if (maxProcess > numberOfProcesses) {
+ debugs(54, DBG_IMPORTANT, "WARNING: 'cpu_affinity_map' has "
+ "non-existing process number(s)");
+ }
+ }
+}
=== added file 'src/CpuAffinity.h'
--- src/CpuAffinity.h 1970-01-01 00:00:00 +0000
+++ src/CpuAffinity.h 2010-09-10 23:31:04 +0000
@@ -0,0 +1,20 @@
+/*
+ * $Id$
+ *
+ */
+
+#ifndef SQUID_CPU_AFFINITY_H
+#define SQUID_CPU_AFFINITY_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
=== added file 'src/CpuAffinityMap.cc'
--- src/CpuAffinityMap.cc 1970-01-01 00:00:00 +0000
+++ src/CpuAffinityMap.cc 2010-09-11 00:28:06 +0000
@@ -0,0 +1,58 @@
+/*
+ * $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)
+{
+ bool error = (aProcesses.size() != aCores.size());
+ for (size_t i = 0; !error && i < aProcesses.size(); ++i) {
+ const int process = aProcesses[i];
+ const int core = aCores[i];
+ if (process <= 0 || core <= 0)
+ error = true;
+ theProcesses.push_back(process);
+ theCores.push_back(core);
+ }
+ return !error;
+}
+
+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 HAVE_CPU_AFFINITY
+ if (core > 0) {
+ cpuAffinitySet = new CpuAffinitySet;
+ cpu_set_t cpuSet;
+ CPU_ZERO(&cpuSet);
+ CPU_SET(core - 1, &cpuSet);
+ cpuAffinitySet->set(cpuSet);
+ }
+#endif
+ return cpuAffinitySet;
+}
=== added file 'src/CpuAffinityMap.h'
--- src/CpuAffinityMap.h 1970-01-01 00:00:00 +0000
+++ src/CpuAffinityMap.h 2010-09-10 23:32:15 +0000
@@ -0,0 +1,35 @@
+/*
+ * $Id$
+ *
+ */
+
+#ifndef SQUID_IPC_CPU_AFFINITY_MAP_H
+#define SQUID_IPC_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_IPC_CPU_AFFINITY_MAP_H
=== added file 'src/CpuAffinitySet.cc'
--- src/CpuAffinitySet.cc 1970-01-01 00:00:00 +0000
+++ src/CpuAffinitySet.cc 2010-09-11 04:53:29 +0000
@@ -0,0 +1,85 @@
+/*
+ * $Id$
+ *
+ * DEBUG: section 54 Interprocess Communication
+ *
+ */
+
+#include "config.h"
+#include "base/TextException.h"
+#include "CpuAffinitySet.h"
+#include "Debug.h"
+#include "util.h"
+
+
+CpuAffinitySet::CpuAffinitySet()
+{
+#if HAVE_CPU_AFFINITY
+ CPU_ZERO(&theCpuSet);
+ CPU_ZERO(&theOrigCpuSet);
+#endif
+}
+
+void
+CpuAffinitySet::apply()
+{
+#if HAVE_CPU_AFFINITY
+ 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);
+#endif
+}
+
+void
+CpuAffinitySet::undo()
+{
+#if HAVE_CPU_AFFINITY
+ 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);
+ }
+#endif
+}
+
+bool
+CpuAffinitySet::applied() const
+{
+#if HAVE_CPU_AFFINITY
+ return (CPU_COUNT(&theOrigCpuSet) > 0);
+#else
+ return false;
+#endif
+}
+
+#if HAVE_CPU_AFFINITY
+void
+CpuAffinitySet::set(const cpu_set_t &aCpuSet)
+{
+ xmemcpy(&theCpuSet, &aCpuSet, sizeof(theCpuSet));
+}
+#endif
=== added file 'src/CpuAffinitySet.h'
--- src/CpuAffinitySet.h 1970-01-01 00:00:00 +0000
+++ src/CpuAffinitySet.h 2010-09-11 00:22:30 +0000
@@ -0,0 +1,41 @@
+/*
+ * $Id$
+ *
+ */
+
+#ifndef SQUID_IPC_CPU_AFFINITY_SET_H
+#define SQUID_IPC_CPU_AFFINITY_SET_H
+
+#include "SquidString.h"
+
+#if HAVE_CPU_AFFINITY && HAVE_SCHED_H
+#include <sched.h>
+#endif
+
+
+/// 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;
+
+#if HAVE_CPU_AFFINITY
+ /// 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
+};
+
+#endif // SQUID_IPC_CPU_AFFINITY_SET_H
=== modified file 'src/Makefile.am'
--- src/Makefile.am 2010-09-11 00:52:48 +0000
+++ src/Makefile.am 2010-09-11 00:53:18 +0000
@@ -274,40 +274,46 @@ squid_SOURCES = \
client_side_reply.cc \
client_side_reply.h \
client_side_request.cc \
client_side_request.h \
ClientInfo.h \
BodyPipe.cc \
BodyPipe.h \
ClientInfo.h \
ClientRequestContext.h \
clientStream.cc \
clientStream.h \
CommIO.h \
CompletionDispatcher.cc \
CompletionDispatcher.h \
$(squid_COMMSOURCES) \
CommRead.h \
ConfigOption.cc \
ConfigParser.cc \
ConfigParser.h \
ConnectionDetail.h \
+ CpuAffinity.cc \
+ CpuAffinity.h \
+ CpuAffinityMap.cc \
+ CpuAffinityMap.h \
+ CpuAffinitySet.cc \
+ CpuAffinitySet.h \
debug.cc \
Debug.h \
defines.h \
$(DELAY_POOL_SOURCE) \
disk.cc \
$(DISKIO_SOURCE) \
dlink.h \
dlink.cc \
$(DNSSOURCE) \
enums.h \
err_type.h \
errorpage.cc \
errorpage.h \
ETag.cc \
event.cc \
event.h \
EventLoop.h \
EventLoop.cc \
external_acl.cc \
ExternalACL.h \
@@ -1118,40 +1124,44 @@ tests_testCacheManager_SOURCES = \
tests/stub_main_cc.cc \
time.cc \
BodyPipe.cc \
cache_manager.cc \
cache_cf.cc \
ProtoPort.cc \
ProtoPort.h \
CacheDigest.cc \
carp.cc \
cbdata.cc \
ChunkedCodingParser.cc \
client_db.cc \
client_side.cc \
client_side_reply.cc \
client_side_request.cc \
ClientInfo.h \
clientStream.cc \
$(squid_COMMSOURCES) \
ConfigOption.cc \
ConfigParser.cc \
+ CpuAffinityMap.cc \
+ CpuAffinityMap.h \
+ CpuAffinitySet.cc \
+ CpuAffinitySet.h \
$(DELAY_POOL_SOURCE) \
disk.cc \
dlink.h \
dlink.cc \
$(DNSSOURCE) \
errorpage.cc \
ETag.cc \
external_acl.cc \
ExternalACLEntry.cc \
fd.cc \
fde.cc \
forward.cc \
fqdncache.cc \
ftp.cc \
gopher.cc \
hier_code.h \
helper.cc \
HelperChildConfig.h \
HelperChildConfig.cc \
$(HTCPSOURCE) \
@@ -1303,40 +1313,44 @@ tests_testEvent_SOURCES = \
tests/stub_main_cc.cc \
time.cc \
BodyPipe.cc \
cache_manager.cc \
cache_cf.cc \
ProtoPort.cc \
ProtoPort.h \
CacheDigest.cc \
carp.cc \
cbdata.cc \
ChunkedCodingParser.cc \
client_db.cc \
client_side.cc \
client_side_reply.cc \
client_side_request.cc \
ClientInfo.h \
clientStream.cc \
$(squid_COMMSOURCES) \
ConfigOption.cc \
ConfigParser.cc \
+ CpuAffinityMap.cc \
+ CpuAffinityMap.h \
+ CpuAffinitySet.cc \
+ CpuAffinitySet.h \
$(DELAY_POOL_SOURCE) \
disk.cc \
dlink.h \
dlink.cc \
$(DNSSOURCE) \
errorpage.cc \
ETag.cc \
external_acl.cc \
ExternalACLEntry.cc \
fd.cc \
fde.cc \
forward.cc \
fqdncache.cc \
ftp.cc \
gopher.cc \
hier_code.h \
helper.cc \
HelperChildConfig.h \
HelperChildConfig.cc \
$(HTCPSOURCE) \
@@ -1459,40 +1473,44 @@ tests_testEventLoop_SOURCES = \
tests/stub_main_cc.cc \
time.cc \
BodyPipe.cc \
cache_manager.cc \
cache_cf.cc \
ProtoPort.cc \
ProtoPort.h \
CacheDigest.cc \
carp.cc \
cbdata.cc \
ChunkedCodingParser.cc \
client_db.cc \
client_side.cc \
client_side_reply.cc \
client_side_request.cc \
ClientInfo.h \
clientStream.cc \
$(squid_COMMSOURCES) \
ConfigOption.cc \
ConfigParser.cc \
+ CpuAffinityMap.cc \
+ CpuAffinityMap.h \
+ CpuAffinitySet.cc \
+ CpuAffinitySet.h \
$(DELAY_POOL_SOURCE) \
disk.cc \
dlink.h \
dlink.cc \
$(DNSSOURCE) \
errorpage.cc \
ETag.cc \
external_acl.cc \
ExternalACLEntry.cc \
fd.cc \
fde.cc \
forward.cc \
fqdncache.cc \
ftp.cc \
gopher.cc \
helper.cc \
HelperChildConfig.h \
HelperChildConfig.cc \
hier_code.h \
$(HTCPSOURCE) \
@@ -1602,40 +1620,44 @@ tests_test_http_range_SOURCES = \
$(ACL_REGISTRATION_SOURCES) \
tests/test_http_range.cc \
BodyPipe.cc \
cache_cf.cc \
ProtoPort.cc \
ProtoPort.h \
cache_manager.cc \
CacheDigest.cc \
carp.cc \
cbdata.cc \
ChunkedCodingParser.cc \
client_db.cc \
client_side.cc \
client_side_reply.cc \
client_side_request.cc \
ClientInfo.h \
clientStream.cc \
$(squid_COMMSOURCES) \
ConfigOption.cc \
ConfigParser.cc \
+ CpuAffinityMap.cc \
+ CpuAffinityMap.h \
+ CpuAffinitySet.cc \
+ CpuAffinitySet.h \
tests/stub_main_cc.cc \
debug.cc \
$(DELAY_POOL_SOURCE) \
disk.cc \
dlink.h \
dlink.cc \
$(DNSSOURCE) \
errorpage.cc \
ETag.cc \
external_acl.cc \
ExternalACLEntry.cc \
fd.cc \
fde.cc \
forward.cc \
fqdncache.cc \
ftp.cc \
gopher.cc \
helper.cc \
HelperChildConfig.h \
HelperChildConfig.cc \
@@ -1765,40 +1787,44 @@ tests_testHttpRequest_SOURCES = \
tests/stub_main_cc.cc \
time.cc \
BodyPipe.cc \
cache_manager.cc \
cache_cf.cc \
ProtoPort.cc \
ProtoPort.h \
CacheDigest.cc \
carp.cc \
cbdata.cc \
ChunkedCodingParser.cc \
client_db.cc \
client_side.cc \
client_side_reply.cc \
client_side_request.cc \
ClientInfo.h \
clientStream.cc \
$(squid_COMMSOURCES) \
ConfigOption.cc \
ConfigParser.cc \
+ CpuAffinityMap.cc \
+ CpuAffinityMap.h \
+ CpuAffinitySet.cc \
+ CpuAffinitySet.h \
$(DELAY_POOL_SOURCE) \
disk.cc \
dlink.h \
dlink.cc \
$(DNSSOURCE) \
errorpage.cc \
ETag.cc \
external_acl.cc \
ExternalACLEntry.cc \
fd.cc \
fde.cc \
forward.cc \
fqdncache.cc \
ftp.cc \
gopher.cc \
helper.cc \
HelperChildConfig.h \
HelperChildConfig.cc \
hier_code.h \
$(HTCPSOURCE) \
@@ -2141,40 +2167,44 @@ tests_testURL_SOURCES = \
tests/stub_main_cc.cc \
time.cc \
BodyPipe.cc \
cache_manager.cc \
cache_cf.cc \
ProtoPort.cc \
ProtoPort.h \
CacheDigest.cc \
carp.cc \
cbdata.cc \
ChunkedCodingParser.cc \
client_db.cc \
client_side.cc \
client_side_reply.cc \
client_side_request.cc \
ClientInfo.h \
clientStream.cc \
$(squid_COMMSOURCES) \
ConfigOption.cc \
ConfigParser.cc \
+ CpuAffinityMap.cc \
+ CpuAffinityMap.h \
+ CpuAffinitySet.cc \
+ CpuAffinitySet.h \
$(DELAY_POOL_SOURCE) \
disk.cc \
dlink.h \
dlink.cc \
$(DNSSOURCE) \
errorpage.cc \
ETag.cc \
external_acl.cc \
ExternalACLEntry.cc \
fd.cc \
fde.cc \
forward.cc \
fqdncache.cc \
ftp.cc \
gopher.cc \
helper.cc \
HelperChildConfig.h \
HelperChildConfig.cc \
hier_code.h \
$(HTCPSOURCE) \
=== modified file 'src/cache_cf.cc'
--- src/cache_cf.cc 2010-08-25 03:08:37 +0000
+++ src/cache_cf.cc 2010-09-10 23:11:43 +0000
@@ -33,40 +33,41 @@
*/
#include "squid.h"
#include "acl/Acl.h"
#include "acl/Gadgets.h"
#include "acl/MethodData.h"
#if USE_ADAPTATION
#include "adaptation/Config.h"
#endif
#if ICAP_CLIENT
#include "adaptation/icap/Config.h"
#endif
#if USE_ECAP
#include "adaptation/ecap/Config.h"
#endif
#include "auth/Config.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"
#endif
#include "HttpRequestMethod.h"
#include "ident/Config.h"
#include "ip/Intercept.h"
#include "ip/QosConfig.h"
#include "ip/tools.h"
#include "log/Config.h"
#include "MemBuf.h"
#include "Parsing.h"
#include "ProtoPort.h"
#include "rfc1738.h"
#if SQUID_SNMP
#include "snmp.h"
#endif
#include "Store.h"
#include "StoreFileSystem.h"
#include "SwapDir.h"
@@ -164,40 +165,45 @@ static void free_IpAddress_list(Ip::Addr
static int check_null_IpAddress_list(const Ip::Address_list *);
#endif /* CURRENTLY_UNUSED */
#endif /* USE_WCCPv2 */
static void parse_http_port_list(http_port_list **);
static void dump_http_port_list(StoreEntry *, const char *, const http_port_list *);
static void free_http_port_list(http_port_list **);
#if USE_SSL
static void parse_https_port_list(https_port_list **);
static void dump_https_port_list(StoreEntry *, const char *, const https_port_list *);
static void free_https_port_list(https_port_list **);
#if 0
static int check_null_https_port_list(const https_port_list *);
#endif
#endif /* USE_SSL */
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);
/*
* LegacyParser is a parser for legacy code that uses the global
* approach. This is static so that it is only exposed to cache_cf.
* Other modules needing access to a ConfigParser should have it
* provided to them in their parserFOO methods.
*/
static ConfigParser LegacyParser = ConfigParser();
void
self_destruct(void)
{
LegacyParser.destruct();
}
static void
update_maxobjsize(void)
{
int i;
@@ -3825,40 +3831,104 @@ free_logformat(logformat ** definitions)
static void
free_access_log(customlog ** definitions)
{
while (*definitions) {
customlog *log = *definitions;
*definitions = log->next;
log->logFormat = NULL;
log->type = CLF_UNKNOWN;
if (log->aclList)
aclDestroyAclList(&log->aclList);
safe_free(log->filename);
xfree(log);
}
}
+/// 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
+ 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) ||
+ !parseNamedIntList(cToken, "cores", cores) ||
+ !(*cpuAffinityMap)->add(processes, cores))
+ self_destruct();
+#else
+ debugs(3, DBG_CRITICAL, "ERROR: Squid built with no CPU affinity support, "
+ "don't set 'cpu_affinity_map'!");
+ self_destruct();
+#endif
+}
+
+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 = 0;
+}
+
#if USE_ADAPTATION
static void
parse_adaptation_service_set_type()
{
Adaptation::Config::ParseServiceSet();
}
static void
parse_adaptation_service_chain_type()
{
Adaptation::Config::ParseServiceChain();
}
static void
parse_adaptation_access_type()
{
Adaptation::Config::ParseAccess(LegacyParser);
}
=== modified file 'src/cf.data.depend'
--- src/cf.data.depend 2010-04-05 10:29:50 +0000
+++ src/cf.data.depend 2010-09-10 19:55:03 +0000
@@ -1,33 +1,34 @@
# type dependencies
access_log acl logformat
acl external_acl_type auth_param
acl_access acl
acl_address acl
acl_b_size_t acl
acl_tos acl
address
authparam
b_int64_t
b_size_t
cachedir cache_replacement_policy
cachemgrpasswd
+CpuAffinityMap
debug
delay_pool_access acl delay_class
delay_pool_class delay_pools
delay_pool_count
delay_pool_rates delay_class
denyinfo acl
eol
externalAclHelper auth_param
HelperChildConfig
hostdomain cache_peer
hostdomaintype cache_peer
http_header_access acl
http_header_replace
http_port_list
https_port_list
adaptation_access_type adaptation_service_set adaptation_service_chain acl icap_service icap_class
adaptation_service_set_type icap_service ecap_service
adaptation_service_chain_type icap_service ecap_service
icap_access_type icap_class acl
icap_class_type icap_service
=== modified file 'src/cf.data.pre'
--- src/cf.data.pre 2010-09-10 20:56:24 +0000
+++ src/cf.data.pre 2010-09-11 04:53:59 +0000
@@ -7055,21 +7055,43 @@ DOC_START
The default "0" means Squid inherits the current ulimit setting.
Note: Changing this requires a restart of Squid. Also
not all comm loops supports large values.
DOC_END
NAME: workers
TYPE: int
LOC: Config.workers
DEFAULT: 1
DOC_START
Number of main Squid processes or "workers" to fork and maintain.
0: "no daemon" mode, like running "squid -N ..."
1: "no SMP" mode, start one main Squid process daemon (default)
N: start N main Squid process daemons (i.e., SMP mode)
In SMP mode, each worker does nearly all what a single Squid daemon
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,...
+
+ Set 1:1 mapping between Squid "worker" processes and CPU cores. E.g.
+
+ cpu_affinity_map process_numbers=1,2,3,4 cores=1,3,5,7
+
+ This will have an effect for kids 1 through 4 only and place them on
+ even cores starting with core #1.
+
+ CPU cores are numbered starting from 1. Requires sched_getaffinity(2)
+ and sched_setaffinity(2) system calls.
+
+ Multiple cpu_affinity_map options are merged.
+
+ See also: workers
+DOC_END
+
EOF
=== modified file 'src/main.cc'
--- src/main.cc 2010-09-08 14:46:42 +0000
+++ src/main.cc 2010-09-11 05:26:20 +0000
@@ -23,40 +23,41 @@
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
*
*/
#include "squid.h"
#include "AccessLogEntry.h"
#if ICAP_CLIENT
#include "adaptation/icap/icap_log.h"
#endif
#include "auth/Gadgets.h"
#include "base/TextException.h"
#include "ConfigParser.h"
+#include "CpuAffinity.h"
#include "errorpage.h"
#include "event.h"
#include "EventLoop.h"
#include "ExternalACL.h"
#include "Store.h"
#include "ICP.h"
#include "ident/Ident.h"
#include "HttpReply.h"
#include "pconn.h"
#include "Mem.h"
#include "acl/Asn.h"
#include "acl/Acl.h"
#include "htcp.h"
#include "StoreFileSystem.h"
#include "DiskIO/DiskIOModule.h"
#include "comm.h"
#include "ipc/Kids.h"
#include "ipc/Coordinator.h"
#include "ipc/Strand.h"
#include "ip/tools.h"
@@ -753,40 +754,44 @@ mainReconfigureFinish(void *)
errorClean();
enter_suid(); /* root to read config file */
// we may have disabled the need for PURGE
if (Config2.onoff.enable_purge)
Config2.onoff.enable_purge = 2;
// parse the config returns a count of errors encountered.
const int oldWorkers = Config.workers;
if ( parseConfigFile(ConfigFile) != 0) {
// for now any errors are a fatal condition...
self_destruct();
}
if (oldWorkers != Config.workers) {
debugs(1, DBG_CRITICAL, "WARNING: Changing 'workers' (from " <<
oldWorkers << " to " << Config.workers <<
") is not supported and ignored");
Config.workers = oldWorkers;
}
+ if (IamPrimaryProcess())
+ CpuAffinityCheck();
+ CpuAffinityReconfigure();
+
setUmask(Config.umask);
Mem::Report();
setEffectiveUser();
_db_init(Debug::cache_log, Debug::debugOptions);
ipcache_restart(); /* clear stuck entries */
fqdncache_restart(); /* sigh, fqdncache too */
parseEtcHosts();
errorInitialize(); /* reload error pages */
accessLogInit();
#if USE_LOADABLE_MODULES
LoadableModulesConfigure(Config.loadable_module_names);
#endif
#if USE_ADAPTATION
bool enableAdaptation = false;
#if ICAP_CLIENT
Adaptation::Icap::TheConfig.finalize();
enableAdaptation = Adaptation::Icap::TheConfig.onoff || enableAdaptation;
#endif
@@ -1393,40 +1398,44 @@ SquidMain(int argc, char **argv)
}
sendSignal();
/* NOTREACHED */
}
if (opt_create_swap_dirs) {
/* chroot if configured to run inside chroot */
if (Config.chroot_dir && chroot(Config.chroot_dir)) {
fatal("failed to chroot");
}
setEffectiveUser();
debugs(0, 0, "Creating Swap Directories");
Store::Root().create();
return 0;
}
+ if (IamPrimaryProcess())
+ CpuAffinityCheck();
+ CpuAffinityInit();
+
if (!opt_no_daemon && Config.workers > 0)
watch_child(argv);
setMaxFD();
/* init comm module */
comm_init();
comm_select_init();
if (opt_no_daemon) {
/* we have to init fdstat here. */
fd_open(0, FD_LOG, "stdin");
fd_open(1, FD_LOG, "stdout");
fd_open(2, FD_LOG, "stderr");
}
#if USE_WIN32_SERVICE
WIN32_svcstatusupdate(SERVICE_START_PENDING, 10000);
=== modified file 'src/protos.h'
--- src/protos.h 2010-08-24 10:35:03 +0000
+++ src/protos.h 2010-09-11 05:33:24 +0000
@@ -566,42 +566,46 @@ SQUIDCEXTERN void enter_suid(void);
SQUIDCEXTERN void no_suid(void);
SQUIDCEXTERN void writePidFile(void);
SQUIDCEXTERN void setSocketShutdownLifetimes(int);
SQUIDCEXTERN void setMaxFD(void);
SQUIDCEXTERN void setSystemLimits(void);
SQUIDCEXTERN void squid_signal(int sig, SIGHDLR *, int flags);
SQUIDCEXTERN pid_t readPidFile(void);
SQUIDCEXTERN void keepCapabilities(void);
SQUIDCEXTERN void BroadcastSignalIfAny(int& sig);
/// whether the current process is the parent of all other Squid processes
SQUIDCEXTERN bool IamMasterProcess();
/**
whether the current process is dedicated to doing things that only
a single process should do, such as PID file maintenance and WCCP
*/
SQUIDCEXTERN bool IamPrimaryProcess();
/// whether the current process coordinates worker processes
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
+/// returns the number of kids
+SQUIDCEXTERN int NumberOfKids();
SQUIDCEXTERN int DebugSignal;
/* AYJ debugs function to show locations being reset with memset() */
SQUIDCEXTERN void *xmemset(void *dst, int, size_t);
SQUIDCEXTERN void debug_trap(const char *);
SQUIDCEXTERN void logsFlush(void);
SQUIDCEXTERN const char *checkNullString(const char *p);
SQUIDCEXTERN void squid_getrusage(struct rusage *r);
SQUIDCEXTERN double rusage_cputime(struct rusage *r);
SQUIDCEXTERN int rusage_maxrss(struct rusage *r);
SQUIDCEXTERN int rusage_pagefaults(struct rusage *r);
SQUIDCEXTERN void releaseServerSockets(void);
SQUIDCEXTERN void PrintRusage(void);
SQUIDCEXTERN void dumpMallocStats(void);
=== modified file 'src/structs.h'
--- src/structs.h 2010-09-10 20:56:24 +0000
+++ src/structs.h 2010-09-10 23:57:42 +0000
@@ -113,40 +113,41 @@ struct ushortlist {
};
struct relist {
char *pattern;
regex_t regex;
relist *next;
};
#if DELAY_POOLS
#include "DelayConfig.h"
#endif
#if USE_ICMP
#include "icmp/IcmpConfig.h"
#endif
#include "HelperChildConfig.h"
/* forward decl for SquidConfig, see RemovalPolicy.h */
+class CpuAffinityMap;
class RemovalPolicySettings;
class external_acl;
class Store;
struct SquidConfig {
struct {
/* These should be for the Store::Root instance.
* this needs pluggable parsing to be done smoothly.
*/
int highWaterMark;
int lowWaterMark;
} Swap;
size_t memMaxSize;
struct {
int64_t min;
int pct;
int64_t max;
} quickAbort;
@@ -594,40 +595,41 @@ struct SquidConfig {
struct {
char *cert;
char *key;
int version;
char *options;
char *cipher;
char *cafile;
char *capath;
char *crlfile;
char *flags;
acl_access *cert_error;
SSL_CTX *sslContext;
} ssl_client;
#endif
char *accept_filter;
int umask;
int max_filedescriptors;
int workers;
+ CpuAffinityMap *cpuAffinityMap;
#if USE_LOADABLE_MODULES
wordlist *loadable_module_names;
#endif
int client_ip_max_connections;
};
SQUIDCEXTERN SquidConfig Config;
struct SquidConfig2 {
struct {
int enable_purge;
int mangle_request_headers;
} onoff;
uid_t effectiveUserID;
gid_t effectiveGroupID;
};
SQUIDCEXTERN SquidConfig2 Config2;
=== modified file 'src/tools.cc'
--- src/tools.cc 2010-08-09 11:06:36 +0000
+++ src/tools.cc 2010-09-11 05:27:48 +0000
@@ -807,67 +807,87 @@ no_suid(void)
#endif
}
bool
IamMasterProcess()
{
return KidIdentifier == 0;
}
bool
IamWorkerProcess()
{
// when there is only one process, it has to be the worker
if (opt_no_daemon || Config.workers == 0)
return true;
return 0 < KidIdentifier && KidIdentifier <= Config.workers;
}
bool
+InDaemonMode()
+{
+ return !opt_no_daemon && Config.workers > 0;
+}
+
+bool
UsingSmp()
{
return !opt_no_daemon && Config.workers > 1;
}
bool
IamCoordinatorProcess()
{
return UsingSmp() && KidIdentifier == Config.workers + 1;
}
bool
IamPrimaryProcess()
{
// when there is only one process, it has to be primary
if (opt_no_daemon || Config.workers == 0)
return true;
// when there is a master and worker process, the master delegates
// primary functions to its only kid
if (Config.workers == 1)
return IamWorkerProcess();
// in SMP mode, multiple kids delegate primary functions to the coordinator
return IamCoordinatorProcess();
}
+int
+NumberOfKids()
+{
+ // no kids in non-daemon mode
+ if (!InDaemonMode())
+ return 0;
+
+ // workers + the coordinator process
+ if (UsingSmp())
+ return Config.workers + 1;
+
+ return Config.workers;
+}
+
void
writePidFile(void)
{
int fd;
const char *f = NULL;
mode_t old_umask;
char buf[32];
if (!IamPrimaryProcess())
return;
if ((f = Config.pidFilename) == NULL)
return;
if (!strcmp(Config.pidFilename, "none"))
return;
enter_suid();
old_umask = umask(022);