gvisor/test/util/memory_util.h

148 lines
4.5 KiB
C
Raw Normal View History

// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef GVISOR_TEST_UTIL_MEMORY_UTIL_H_
#define GVISOR_TEST_UTIL_MEMORY_UTIL_H_
#include <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <sys/mman.h>
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include "test/util/logging.h"
#include "test/util/posix_error.h"
#include "test/util/save_util.h"
#include "test/util/test_util.h"
namespace gvisor {
namespace testing {
// RAII type for mmap'ed memory. Only usable in tests due to use of a test-only
// macro that can't be named without invoking the presubmit's wrath.
class Mapping {
public:
// Constructs a mapping that owns nothing.
Mapping() = default;
// Constructs a mapping that owns the mmapped memory [ptr, ptr+len). Most
// users should use Mmap or MmapAnon instead.
Mapping(void* ptr, size_t len) : ptr_(ptr), len_(len) {}
Mapping(Mapping&& orig) : ptr_(orig.ptr_), len_(orig.len_) { orig.release(); }
Mapping& operator=(Mapping&& orig) {
ptr_ = orig.ptr_;
len_ = orig.len_;
orig.release();
return *this;
}
Mapping(Mapping const&) = delete;
Mapping& operator=(Mapping const&) = delete;
~Mapping() { reset(); }
void* ptr() const { return ptr_; }
size_t len() const { return len_; }
// Returns a pointer to the end of the mapping. Useful for when the mapping
// is used as a thread stack.
void* endptr() const { return reinterpret_cast<void*>(addr() + len_); }
// Returns the start of this mapping cast to uintptr_t for ease of pointer
// arithmetic.
uintptr_t addr() const { return reinterpret_cast<uintptr_t>(ptr_); }
// Returns the end of this mapping cast to uintptr_t for ease of pointer
// arithmetic.
uintptr_t endaddr() const { return reinterpret_cast<uintptr_t>(endptr()); }
// Returns this mapping as a StringPiece for ease of comparison.
//
// This function is named view in anticipation of the eventual replacement of
// StringPiece with std::string_view.
absl::string_view view() const {
return absl::string_view(static_cast<char const*>(ptr_), len_);
}
// These are both named reset for consistency with standard smart pointers.
void reset(void* ptr, size_t len) {
if (len_) {
TEST_PCHECK(munmap(ptr_, len_) == 0);
}
ptr_ = ptr;
len_ = len;
}
void reset() { reset(nullptr, 0); }
void release() {
ptr_ = nullptr;
len_ = 0;
}
private:
void* ptr_ = nullptr;
size_t len_ = 0;
};
// Wrapper around mmap(2) that returns a Mapping.
inline PosixErrorOr<Mapping> Mmap(void* addr, size_t length, int prot,
int flags, int fd, off_t offset) {
void* ptr = mmap(addr, length, prot, flags, fd, offset);
if (ptr == MAP_FAILED) {
return PosixError(
errno, absl::StrFormat("mmap(%p, %d, %x, %x, %d, %d)", addr, length,
prot, flags, fd, offset));
}
MaybeSave();
return Mapping(ptr, length);
}
// Convenience wrapper around Mmap for anonymous mappings.
inline PosixErrorOr<Mapping> MmapAnon(size_t length, int prot, int flags) {
return Mmap(nullptr, length, prot, flags | MAP_ANONYMOUS, -1, 0);
}
Deflake aio_test. - Most AIO tests call io_setup(nr_events = 128). sizeof(struct io_event) (128*32 = 4096). However, the actual size of the mapping created by io_setup() is determined by: (from fs/aio.c:ioctx_alloc()) /* * We keep track of the number of available ringbuffer slots, to prevent * overflow (reqs_available), and we also use percpu counters for this. * * So since up to half the slots might be on other cpu's percpu counters * and unavailable, double nr_events so userspace sees what they * expected: additionally, we move req_batch slots to/from percpu * counters at a time, so make sure that isn't 0: */ nr_events = max(nr_events, num_possible_cpus() * 4); nr_events *= 2; (from fs/aio.c:aio_setup_ring()) /* Compensate for the ring buffer's head/tail overlap entry */ nr_events += 2; /* 1 is required, 2 for good luck */ size = sizeof(struct aio_ring); size += sizeof(struct io_event) * nr_events; nr_pages = PFN_UP(size); When we mremap() only the first page of a multi-page AIO ring buffer mapping, fs/aio.c:aio_ring_mremap() updates struct kioctx::mmap_base - but struct kioctx::mmap_size is untouched, so sys_io_destroy() => kill_ioctx() vm_unmaps() the mremapped page, plus some number of pages after it. Just get the actual size of the mapping from /proc/self/maps. - Delete test case MremapOver; while it is correct that Linux will not complain if you overwrite the AIO ring buffer with another mapping, it won't actually work in the sense that AIO events will not be written to the new mapping, because Linux stores the struct pages of the ring buffer in struct kioctx::ring_pages and writes to those through kmap() rather than using userspace addresses. - Don't munmap() after mremap(MREMAP_FIXED) returns EFAULT; see new comment in factored-out test case MremapExpansion. PiperOrigin-RevId: 267482903
2019-09-05 23:35:23 +00:00
// Wrapper for mremap that returns a PosixErrorOr<>, since the return type of
// void* isn't directly compatible with SyscallSucceeds.
inline PosixErrorOr<void*> Mremap(void* old_address, size_t old_size,
size_t new_size, int flags,
void* new_address) {
void* rv = mremap(old_address, old_size, new_size, flags, new_address);
if (rv == MAP_FAILED) {
return PosixError(errno, "mremap failed");
}
return rv;
}
// Returns true if the page containing addr is mapped.
inline bool IsMapped(uintptr_t addr) {
int const rv = msync(reinterpret_cast<void*>(addr & ~(kPageSize - 1)),
kPageSize, MS_ASYNC);
if (rv == 0) {
return true;
}
TEST_PCHECK_MSG(errno == ENOMEM, "msync failed with unexpected errno");
return false;
}
} // namespace testing
} // namespace gvisor
#endif // GVISOR_TEST_UTIL_MEMORY_UTIL_H_