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