PageRenderTime 27ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/src/DiskIO/Mmapped/MmappedFile.cc

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