1/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
2
3Licensed under the Apache License, Version 2.0 (the "License");
4you may not use this file except in compliance with the License.
5You may obtain a copy of the License at
6
7 http://www.apache.org/licenses/LICENSE-2.0
8
9Unless required by applicable law or agreed to in writing, software
10distributed under the License is distributed on an "AS IS" BASIS,
11WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12See the License for the specific language governing permissions and
13limitations under the License.
14==============================================================================*/
15
16#include <fcntl.h>
17#include <stddef.h>
18#include <sys/mman.h>
19#include <sys/stat.h>
20#include <unistd.h>
21
22#include <cerrno>
23
24#include "tensorflow/lite/allocation.h"
25#include "tensorflow/lite/core/api/error_reporter.h"
26
27namespace tflite {
28namespace {
29
30size_t GetFdSizeBytes(int fd) {
31 if (fd < 0) {
32 return 0;
33 }
34
35 struct stat fd_stat;
36 if (fstat(fd, &fd_stat) != 0) {
37 return 0;
38 }
39
40 return fd_stat.st_size;
41}
42
43} // namespace
44
45MMAPAllocation::MMAPAllocation(const char* filename,
46 ErrorReporter* error_reporter)
47 : MMAPAllocation(error_reporter, open(filename, O_RDONLY)) {
48 if (mmap_fd_ == -1) {
49 TF_LITE_REPORT_ERROR(error_reporter, "Could not open '%s'.", filename);
50 }
51}
52
53MMAPAllocation::MMAPAllocation(int fd, ErrorReporter* error_reporter)
54 : MMAPAllocation(error_reporter, dup(fd)) {
55 if (mmap_fd_ == -1) {
56 TF_LITE_REPORT_ERROR(error_reporter, "Failed to dup '%d' file descriptor.",
57 fd);
58 }
59}
60
61MMAPAllocation::MMAPAllocation(int fd, size_t offset, size_t length,
62 ErrorReporter* error_reporter)
63 : MMAPAllocation(error_reporter, dup(fd), offset, length) {
64 if (mmap_fd_ == -1) {
65 TF_LITE_REPORT_ERROR(error_reporter, "Failed to dup '%d' file descriptor.",
66 fd);
67 }
68}
69
70MMAPAllocation::MMAPAllocation(ErrorReporter* error_reporter, int owned_fd)
71 : MMAPAllocation(error_reporter, owned_fd, /*offset=*/0,
72 /*length=*/GetFdSizeBytes(owned_fd)) {}
73
74MMAPAllocation::MMAPAllocation(ErrorReporter* error_reporter, int owned_fd,
75 size_t offset, size_t length)
76 : Allocation(error_reporter, Allocation::Type::kMMap),
77 mmap_fd_(owned_fd),
78 mmapped_buffer_(MAP_FAILED),
79 buffer_size_bytes_(length) {
80 if (owned_fd < 0) {
81 return;
82 }
83
84#ifdef __ANDROID__
85 static int pagesize = getpagesize();
86#else
87 static int pagesize = sysconf(_SC_PAGE_SIZE);
88#endif
89
90 offset_in_buffer_ = offset % pagesize;
91
92 size_t file_size = GetFdSizeBytes(mmap_fd_);
93 if (length + offset > file_size) {
94 TF_LITE_REPORT_ERROR(error_reporter,
95 "Asked to mmap '%d' bytes from fd '%d' at offset "
96 "'%d'. This is over the length of file '%d'.",
97 length, mmap_fd_, offset, file_size);
98 return;
99 }
100
101 mmapped_buffer_ =
102 mmap(nullptr, /*__len=*/length + offset_in_buffer_, PROT_READ, MAP_SHARED,
103 mmap_fd_, /*__offset=*/offset - offset_in_buffer_);
104 if (mmapped_buffer_ == MAP_FAILED) {
105 TF_LITE_REPORT_ERROR(error_reporter,
106 "Mmap of '%d' at offset '%d' failed with error '%d'.",
107 mmap_fd_, offset, errno);
108 return;
109 }
110}
111
112MMAPAllocation::~MMAPAllocation() {
113 if (valid()) {
114 munmap(const_cast<void*>(mmapped_buffer_),
115 buffer_size_bytes_ + offset_in_buffer_);
116 }
117 if (mmap_fd_ >= 0) {
118 close(mmap_fd_);
119 }
120}
121
122const void* MMAPAllocation::base() const {
123 return reinterpret_cast<const void*>(
124 reinterpret_cast<const char*>(mmapped_buffer_) + offset_in_buffer_);
125}
126
127size_t MMAPAllocation::bytes() const { return buffer_size_bytes_; }
128
129bool MMAPAllocation::valid() const { return mmapped_buffer_ != MAP_FAILED; }
130
131bool MMAPAllocation::IsSupported() { return true; }
132
133} // namespace tflite
134