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