]> git.ipfire.org Git - thirdparty/squid.git/blame - src/DiskIO/Mmapped/MmappedFile.cc
Boilerplate: update copyright blurbs on src/
[thirdparty/squid.git] / src / DiskIO / Mmapped / MmappedFile.cc
CommitLineData
e2851fe7 1/*
bbc27441
AJ
2 * Copyright (C) 1996-2014 The Squid Software Foundation and contributors
3 *
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.
e2851fe7
AR
7 */
8
bbc27441
AJ
9/* DEBUG: section 47 Store Directory Routines */
10
f7f3304a 11#include "squid.h"
582c2af2 12#include "Debug.h"
602d9612 13#include "disk.h"
e2851fe7 14#include "DiskIO/IORequestor.h"
9199139f 15#include "DiskIO/Mmapped/MmappedFile.h"
e2851fe7
AR
16#include "DiskIO/ReadRequest.h"
17#include "DiskIO/WriteRequest.h"
582c2af2 18#include "globals.h"
582c2af2 19
1a30fdf5 20#include <cerrno>
582c2af2 21#if HAVE_SYS_MMAN_H
9199139f 22#include <sys/mman.h>
582c2af2
FC
23#endif
24#if HAVE_SYS_STAT_H
25#include <sys/stat.h>
26#endif
e2851fe7 27
def2dae1
AJ
28// Some systems such as Hurd provide mmap() API but do not support MAP_NORESERVE
29#ifndef MAP_NORESERVE
30#define MAP_NORESERVE 0
31#endif
32
e2851fe7
AR
33CBDATA_CLASS_INIT(MmappedFile);
34
35// helper class to deal with mmap(2) offset alignment and other low-level specs
9199139f
AR
36class Mmapping
37{
e2851fe7
AR
38public:
39 Mmapping(int fd, size_t length, int prot, int flags, off_t offset);
40 ~Mmapping();
41
42 void *map(); ///< calls mmap(2); returns usable buffer or nil on failure
43 bool unmap(); ///< unmaps previously mapped buffer, if any
44
45private:
46 const int fd; ///< descriptor of the mmapped file
47 const size_t length; ///< user-requested data length, needed for munmap
48 const int prot; ///< mmap(2) "protection" flags
49 const int flags; ///< other mmap(2) flags
50 const off_t offset; ///< user-requested data offset
51
52 off_t delta; ///< mapped buffer increment to hit user offset
53 void *buf; ///< buffer returned by mmap, needed for munmap
54};
55
e2851fe7 56MmappedFile::MmappedFile(char const *aPath): fd(-1),
9199139f 57 minOffset(0), maxOffset(-1), error_(false)
e2851fe7
AR
58{
59 assert(aPath);
60 path_ = xstrdup(aPath);
61 debugs(79,5, HERE << this << ' ' << path_);
62}
63
64MmappedFile::~MmappedFile()
65{
66 safe_free(path_);
67 doClose();
68}
69
70// XXX: almost a copy of BlockingFile::open
71void
72MmappedFile::open(int flags, mode_t mode, RefCount<IORequestor> callback)
73{
74 assert(fd < 0);
75
76 /* Simulate async calls */
77 fd = file_open(path_ , flags);
78 ioRequestor = callback;
79
80 if (fd < 0) {
81 debugs(79,3, HERE << "open error: " << xstrerror());
82 error_ = true;
9199139f 83 } else {
cb4185f1 84 ++store_open_disk_fd;
e2851fe7
AR
85 debugs(79,3, HERE << "FD " << fd);
86
87 // setup mapping boundaries
88 struct stat sb;
89 if (fstat(fd, &sb) == 0)
90 maxOffset = sb.st_size; // we do not expect it to change
9199139f 91 }
e2851fe7
AR
92
93 callback->ioCompletedNotification();
94}
95
96/**
97 * Alias for MmappedFile::open(...)
98 \copydoc MmappedFile::open(int flags, mode_t mode, RefCount<IORequestor> callback)
99 */
100void
101MmappedFile::create(int flags, mode_t mode, RefCount<IORequestor> callback)
102{
103 /* We use the same logic path for open */
104 open(flags, mode, callback);
105}
106
107void MmappedFile::doClose()
108{
109 if (fd >= 0) {
110 file_close(fd);
111 fd = -1;
5e263176 112 --store_open_disk_fd;
9199139f 113 }
e2851fe7
AR
114}
115
116void
117MmappedFile::close()
118{
119 debugs(79, 3, HERE << this << " closing for " << ioRequestor);
120 doClose();
121 assert(ioRequestor != NULL);
122 ioRequestor->closeCompleted();
123}
124
125bool
126MmappedFile::canRead() const
127{
128 return fd >= 0;
129}
130
131bool
132MmappedFile::canWrite() const
133{
134 return fd >= 0;
135}
136
137bool
138MmappedFile::error() const
139{
140 return error_;
141}
142
143void
144MmappedFile::read(ReadRequest *aRequest)
145{
146 debugs(79,3, HERE << "(FD " << fd << ", " << aRequest->len << ", " <<
9199139f 147 aRequest->offset << ")");
e2851fe7
AR
148
149 assert(fd >= 0);
150 assert(ioRequestor != NULL);
e2851fe7
AR
151 assert(aRequest->len > 0); // TODO: work around mmap failures on zero-len?
152 assert(aRequest->offset >= 0);
153 assert(!error_); // TODO: propagate instead?
154
155 assert(minOffset < 0 || minOffset <= aRequest->offset);
db9958a9 156 assert(maxOffset < 0 || static_cast<uint64_t>(aRequest->offset + aRequest->len) <= static_cast<uint64_t>(maxOffset));
e2851fe7
AR
157
158 Mmapping mapping(fd, aRequest->len, PROT_READ, MAP_PRIVATE | MAP_NORESERVE,
9199139f 159 aRequest->offset);
e2851fe7
AR
160
161 bool done = false;
9199139f 162 if (void *buf = mapping.map()) {
e2851fe7
AR
163 memcpy(aRequest->buf, buf, aRequest->len);
164 done = mapping.unmap();
9199139f 165 }
e2851fe7
AR
166 error_ = !done;
167
168 const ssize_t rlen = error_ ? -1 : (ssize_t)aRequest->len;
9199139f 169 const int errflag = error_ ? DISK_ERROR :DISK_OK;
e2851fe7
AR
170 ioRequestor->readCompleted(aRequest->buf, rlen, errflag, aRequest);
171}
172
173void
174MmappedFile::write(WriteRequest *aRequest)
175{
176 debugs(79,3, HERE << "(FD " << fd << ", " << aRequest->len << ", " <<
9199139f 177 aRequest->offset << ")");
e2851fe7
AR
178
179 assert(fd >= 0);
180 assert(ioRequestor != NULL);
e2851fe7
AR
181 assert(aRequest->len > 0); // TODO: work around mmap failures on zero-len?
182 assert(aRequest->offset >= 0);
183 assert(!error_); // TODO: propagate instead?
184
185 assert(minOffset < 0 || minOffset <= aRequest->offset);
db9958a9 186 assert(maxOffset < 0 || static_cast<uint64_t>(aRequest->offset + aRequest->len) <= static_cast<uint64_t>(maxOffset));
e2851fe7
AR
187
188 const ssize_t written =
189 pwrite(fd, aRequest->buf, aRequest->len, aRequest->offset);
190 if (written < 0) {
e0236918 191 debugs(79, DBG_IMPORTANT, HERE << "error: " << xstrerr(errno));
e2851fe7 192 error_ = true;
9199139f 193 } else if (static_cast<size_t>(written) != aRequest->len) {
e0236918 194 debugs(79, DBG_IMPORTANT, HERE << "problem: " << written << " < " << aRequest->len);
e2851fe7
AR
195 error_ = true;
196 }
197
198 if (aRequest->free_func)
199 (aRequest->free_func)(const_cast<char*>(aRequest->buf)); // broken API?
200
201 if (!error_) {
202 debugs(79,5, HERE << "wrote " << aRequest->len << " to FD " << fd << " at " << aRequest->offset);
203 } else {
204 doClose();
9199139f 205 }
e2851fe7
AR
206
207 const ssize_t rlen = error_ ? 0 : (ssize_t)aRequest->len;
9199139f 208 const int errflag = error_ ? DISK_ERROR :DISK_OK;
e2851fe7
AR
209 ioRequestor->writeCompleted(errflag, rlen, aRequest);
210}
211
212/// we only support blocking I/O
213bool
214MmappedFile::ioInProgress() const
215{
216 return false;
217}
218
219Mmapping::Mmapping(int aFd, size_t aLength, int aProt, int aFlags, off_t anOffset):
9199139f
AR
220 fd(aFd), length(aLength), prot(aProt), flags(aFlags), offset(anOffset),
221 delta(-1), buf(NULL)
e2851fe7
AR
222{
223}
224
225Mmapping::~Mmapping()
226{
227 if (buf)
228 unmap();
229}
230
231void *
232Mmapping::map()
233{
234 // mmap(2) requires that offset is a multiple of the page size
235 static const int pageSize = getpagesize();
236 delta = offset % pageSize;
237
238 buf = mmap(NULL, length + delta, prot, flags, fd, offset - delta);
239
240 if (buf == MAP_FAILED) {
241 const int errNo = errno;
242 debugs(79,3, HERE << "error FD " << fd << "mmap(" << length << '+' <<
9199139f 243 delta << ", " << offset << '-' << delta << "): " << xstrerr(errNo));
e2851fe7
AR
244 buf = NULL;
245 return NULL;
246 }
247
248 return static_cast<char*>(buf) + delta;
249}
250
251bool
252Mmapping::unmap()
253{
254 debugs(79,9, HERE << "FD " << fd <<
9199139f 255 " munmap(" << buf << ", " << length << '+' << delta << ')');
e2851fe7
AR
256
257 if (!buf) // forgot or failed to map
258 return false;
259
260 const bool error = munmap(buf, length + delta) != 0;
261 if (error) {
262 const int errNo = errno;
263 debugs(79,3, HERE << "error FD " << fd <<
9199139f
AR
264 " munmap(" << buf << ", " << length << '+' << delta << "): " <<
265 "): " << xstrerr(errNo));
e2851fe7
AR
266 }
267 buf = NULL;
268 return !error;
269}
270
271// TODO: check MAP_NORESERVE, consider MAP_POPULATE and MAP_FIXED