]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/ipc/mem/Segment.cc
2 * Copyright (C) 1996-2020 The Squid Software Foundation and contributors
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
9 /* DEBUG: section 54 Interprocess Communication */
12 #include "base/TextException.h"
13 #include "compat/shm.h"
16 #include "ipc/mem/Segment.h"
17 #include "sbuf/SBuf.h"
18 #include "SquidConfig.h"
34 // test cases change this
35 const char *Ipc::Mem::Segment::BasePath
= DEFAULT_STATEDIR
;
38 Ipc::Mem::Segment::reserve(size_t chunkSize
)
41 // check for overflows
42 // chunkSize >= 0 may result in warnings on systems where off_t is unsigned
43 assert(!chunkSize
|| static_cast<off_t
>(chunkSize
) > 0);
44 assert(static_cast<off_t
>(chunkSize
) <= theSize
);
45 assert(theReserved
<= theSize
- static_cast<off_t
>(chunkSize
));
46 void *result
= reinterpret_cast<char*>(theMem
) + theReserved
;
47 theReserved
+= chunkSize
;
52 Ipc::Mem::Segment::Name(const SBuf
&prefix
, const char *suffix
)
56 result
.append(suffix
);
62 Ipc::Mem::Segment::Segment(const char *const id
):
63 theFD(-1), theName(GenerateName(id
)), theMem(NULL
),
64 theSize(0), theReserved(0), doUnlink(false)
68 Ipc::Mem::Segment::~Segment()
72 if (close(theFD
) != 0) {
74 debugs(54, 5, "close " << theName
<< ": " << xstrerr(xerrno
));
81 // fake Ipc::Mem::Segment::Enabled (!HAVE_SHM) is more selective
83 Ipc::Mem::Segment::Enabled()
89 Ipc::Mem::Segment::create(const off_t aSize
)
96 // Why a brand new segment? A Squid crash may leave a reusable segment, but
97 // our placement-new code requires an all-0s segment. We could truncate and
98 // resize the old segment, but OS X does not allow using O_TRUNC with
99 // shm_open() and does not support ftruncate() for old segments.
100 if (!createFresh(xerrno
) && xerrno
== EEXIST
) {
106 debugs(54, 5, "shm_open " << theName
<< ": " << xstrerr(xerrno
));
107 fatalf("Ipc::Mem::Segment::create failed to shm_open(%s): %s\n",
108 theName
.termedBuf(), xstrerr(xerrno
));
111 if (ftruncate(theFD
, aSize
)) {
114 debugs(54, 5, "ftruncate " << theName
<< ": " << xstrerr(xerrno
));
115 fatalf("Ipc::Mem::Segment::create failed to ftruncate(%s): %s\n",
116 theName
.termedBuf(), xstrerr(xerrno
));
118 // We assume that the shm_open(O_CREAT)+ftruncate() combo zeros the segment.
120 theSize
= statSize("Ipc::Mem::Segment::create");
122 // OS X will round up to a full page, so not checking for exact size match.
123 assert(theSize
>= aSize
);
128 debugs(54, 3, "created " << theName
<< " segment: " << theSize
);
133 Ipc::Mem::Segment::open(const bool unlinkWhenDone
)
137 theFD
= shm_open(theName
.termedBuf(), O_RDWR
, 0);
140 debugs(54, 5, "shm_open " << theName
<< ": " << xstrerr(xerrno
));
141 fatalf("Ipc::Mem::Segment::open failed to shm_open(%s): %s\n",
142 theName
.termedBuf(), xstrerr(xerrno
));
145 theSize
= statSize("Ipc::Mem::Segment::open");
146 doUnlink
= unlinkWhenDone
;
148 debugs(54, 3, HERE
<< "opened " << theName
<< " segment: " << theSize
);
153 /// Creates a brand new shared memory segment and returns true.
154 /// Fails and returns false if there exist an old segment with the same name.
156 Ipc::Mem::Segment::createFresh(int &xerrno
)
158 theFD
= shm_open(theName
.termedBuf(),
159 O_EXCL
| O_CREAT
| O_RDWR
,
165 /// Map the shared memory segment to the process memory space.
167 Ipc::Mem::Segment::attach()
172 // mmap() accepts size_t for the size; we give it off_t which might
173 // be bigger; assert overflows until we support multiple mmap()s?
174 assert(theSize
== static_cast<off_t
>(static_cast<size_t>(theSize
)));
177 mmap(NULL
, theSize
, PROT_READ
| PROT_WRITE
, MAP_SHARED
, theFD
, 0);
178 if (p
== MAP_FAILED
) {
180 debugs(54, 5, "mmap " << theName
<< ": " << xstrerr(xerrno
));
181 fatalf("Ipc::Mem::Segment::attach failed to mmap(%s): %s\n",
182 theName
.termedBuf(), xstrerr(xerrno
));
189 /// Unmap the shared memory segment from the process memory space.
191 Ipc::Mem::Segment::detach()
196 if (munmap(theMem
, theSize
)) {
198 debugs(54, 5, "munmap " << theName
<< ": " << xstrerr(xerrno
));
199 fatalf("Ipc::Mem::Segment::detach failed to munmap(%s): %s\n",
200 theName
.termedBuf(), xstrerr(xerrno
));
205 /// Lock the segment into RAM, ensuring that the OS has enough RAM for it [now]
206 /// and preventing segment bytes from being swapped out to disk later by the OS.
208 Ipc::Mem::Segment::lock()
210 if (!Config
.shmLocking
) {
211 debugs(54, 5, "mlock(2)-ing disabled");
215 #if defined(_POSIX_MEMLOCK_RANGE)
216 debugs(54, 7, "mlock(" << theName
<< ',' << theSize
<< ") starts");
217 if (mlock(theMem
, theSize
) != 0) {
218 const int savedError
= errno
;
219 fatalf("shared_memory_locking on but failed to mlock(%s, %" PRId64
"): %s\n",
220 theName
.termedBuf(),static_cast<int64_t>(theSize
), xstrerr(savedError
));
222 // TODO: Warn if it took too long.
223 debugs(54, 7, "mlock(" << theName
<< ',' << theSize
<< ") OK");
225 debugs(54, 5, "insufficient mlock(2) support");
226 if (Config
.shmLocking
.configured()) { // set explicitly
227 static bool warnedOnce
= false;
229 debugs(54, DBG_IMPORTANT
, "ERROR: insufficient mlock(2) support prevents " <<
230 "honoring `shared_memory_locking on`. " <<
231 "If you lack RAM, kernel will kill Squid later.");
239 Ipc::Mem::Segment::unlink()
241 if (shm_unlink(theName
.termedBuf()) != 0) {
243 debugs(54, 5, "shm_unlink(" << theName
<< "): " << xstrerr(xerrno
));
245 debugs(54, 3, "unlinked " << theName
<< " segment");
248 /// determines the size of the underlying "file"
250 Ipc::Mem::Segment::statSize(const char *context
) const
255 memset(&s
, 0, sizeof(s
));
257 if (fstat(theFD
, &s
) != 0) {
259 debugs(54, 5, context
<< " fstat " << theName
<< ": " << xstrerr(xerrno
));
260 fatalf("Ipc::Mem::Segment::statSize: %s failed to fstat(%s): %s\n",
261 context
, theName
.termedBuf(), xstrerr(xerrno
));
267 /// Generate name for shared memory segment. Starts with a prefix required
268 /// for cross-platform portability and replaces all slashes in ID with dots.
270 Ipc::Mem::Segment::GenerateName(const char *id
)
272 assert(BasePath
&& *BasePath
);
273 static const bool nameIsPath
= shm_portable_segment_name_is_path();
276 name
.append(BasePath
);
277 if (name
[name
.size()-1] != '/')
281 name
.append(service_name
.c_str());
285 // append id, replacing slashes with dots
286 for (const char *slash
= strchr(id
, '/'); slash
; slash
= strchr(id
, '/')) {
288 name
.append(id
, slash
- id
);
295 name
.append(".shm"); // to distinguish from non-segments when nameIsPath
303 typedef std::map
<String
, Ipc::Mem::Segment
*> SegmentMap
;
304 static SegmentMap Segments
;
306 Ipc::Mem::Segment::Segment(const char *const id
):
307 theName(id
), theMem(NULL
), theSize(0), theReserved(0), doUnlink(false)
311 Ipc::Mem::Segment::~Segment()
314 delete [] static_cast<char *>(theMem
);
316 Segments
.erase(theName
);
317 debugs(54, 3, HERE
<< "unlinked " << theName
<< " fake segment");
322 Ipc::Mem::Segment::Enabled()
324 return !UsingSmp() && IamWorkerProcess();
328 Ipc::Mem::Segment::create(const off_t aSize
)
332 checkSupport("Fake segment creation");
334 const bool inserted
= Segments
.insert(std::make_pair(theName
, this)).second
;
336 fatalf("Duplicate fake segment creation: %s", theName
.termedBuf());
338 theMem
= new char[aSize
];
342 debugs(54, 3, HERE
<< "created " << theName
<< " fake segment: " << theSize
);
346 Ipc::Mem::Segment::open()
349 checkSupport("Fake segment open");
351 const SegmentMap::const_iterator i
= Segments
.find(theName
);
352 if (i
== Segments
.end())
353 fatalf("Fake segment not found: %s", theName
.termedBuf());
355 const Segment
&segment
= *i
->second
;
356 theMem
= segment
.theMem
;
357 theSize
= segment
.theSize
;
359 debugs(54, 3, HERE
<< "opened " << theName
<< " fake segment: " << theSize
);
363 Ipc::Mem::Segment::checkSupport(const char *const context
)
366 debugs(54, 5, HERE
<< context
<<
367 ": True shared memory segments are not supported. "
368 "Cannot fake shared segments in SMP config.");
369 fatalf("Ipc::Mem::Segment: Cannot fake shared segments in SMP config (%s)\n",
377 Ipc::Mem::RegisteredRunner::useConfig()
379 // If Squid is built with real segments, we create() real segments
380 // in the master process only. Otherwise, we create() fake
381 // segments in each worker process. We assume that only workers
382 // need and can work with fake segments.
384 if (IamMasterProcess())
386 if (IamWorkerProcess())
390 // we assume that master process does not need shared segments
391 // unless it is also a worker
392 if (!InDaemonMode() || !IamMasterProcess())