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