1 /* 2 This file is part of Android File Transfer For Linux. 3 Copyright (C) 2015-2020 Vladimir Menshakov 4 5 This library is free software; you can redistribute it and/or modify it 6 under the terms of the GNU Lesser General Public License as published by 7 the Free Software Foundation; either version 2.1 of the License, 8 or (at your option) any later version. 9 10 This library is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 Lesser General Public License for more details. 14 15 You should have received a copy of the GNU Lesser General Public License 16 along with this library; if not, write to the Free Software Foundation, 17 Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 */ 19 20 #ifndef AFTL_MTP_BACKEND_LINUX_USB_BUFFERALLOCATOR_H 21 #define AFTL_MTP_BACKEND_LINUX_USB_BUFFERALLOCATOR_H 22 23 #include <mtp/ByteArray.h> 24 #include <mtp/types.h> 25 #include <mtp/log.h> 26 #include <Exception.h> 27 #include <array> 28 29 #include <sys/mman.h> 30 #include <unistd.h> 31 32 namespace mtp { namespace usb 33 { 34 class BufferAllocator; 35 class Buffer 36 { 37 u8 * _data; 38 size_t _size; 39 40 public: Buffer(u8 * data,size_t size)41 Buffer(u8 *data, size_t size): _data(data), _size(size) 42 { } 43 GetData()44 u8 * GetData() const 45 { return _data; } GetSize()46 size_t GetSize() const 47 { return _size; } 48 }; 49 50 class BufferAllocator : Noncopyable 51 { 52 static constexpr size_t Buffers = 16; //for parallel endpoint access 53 static constexpr size_t BufferSize = 64 * 1024; 54 55 std::mutex _mutex; 56 int _fd; 57 long _pageSize; 58 u8 * _buffer; 59 size_t _bufferSize; 60 ByteArray _normalBuffer; 61 62 std::array<bool, Buffers> _bufferAllocated; 63 AllocateNormalBuffer()64 void AllocateNormalBuffer() 65 { 66 _fd = -1; 67 _normalBuffer.resize(Buffers * BufferSize); 68 _buffer = _normalBuffer.data(); 69 _bufferSize = _normalBuffer.size(); 70 } 71 72 public: BufferAllocator(int fd)73 BufferAllocator(int fd): _fd(fd), _pageSize(sysconf(_SC_PAGESIZE)), _buffer(nullptr), _bufferSize(0), _bufferAllocated() 74 { 75 if (_pageSize <= 0) 76 throw posix::Exception("sysconf(_SC_PAGESIZE)"); 77 debug("page size = ", _pageSize); 78 } 79 ~BufferAllocator()80 ~BufferAllocator() 81 { 82 if (_fd >= 0) 83 munmap(_buffer, _bufferSize); 84 } 85 Free(Buffer & buffer)86 void Free(Buffer &buffer) 87 { 88 scoped_mutex_lock l(_mutex); 89 size_t index = (buffer.GetData() - _buffer) / BufferSize; 90 _bufferAllocated.at(index) = false; 91 } 92 Allocate(size_t size)93 Buffer Allocate(size_t size) 94 { 95 scoped_mutex_lock l(_mutex); 96 if (!_buffer) 97 { 98 _bufferSize = (BufferSize + _pageSize - 1) / _pageSize * _pageSize; 99 if (_fd >= 0) 100 { 101 try 102 { 103 auto buffer = static_cast<u8 *>(mmap(nullptr, _bufferSize * Buffers, PROT_READ | PROT_WRITE, MAP_SHARED, _fd, 0)); 104 if (buffer == MAP_FAILED) 105 throw posix::Exception("mmap failed"); 106 107 _buffer = buffer; 108 debug("mapped buffer of ", _bufferSize * Buffers, " bytes to ", static_cast<void *>(_buffer)); 109 } 110 catch(const std::exception &ex) 111 { 112 error("zerocopy allocator failed: ", ex.what()); 113 AllocateNormalBuffer(); 114 } 115 } 116 else 117 AllocateNormalBuffer(); 118 } 119 if (size > BufferSize) 120 size = BufferSize; 121 122 for(size_t i = 0; i < _bufferAllocated.size(); ++i) 123 { 124 if (!_bufferAllocated[i]) 125 { 126 _bufferAllocated[i] = true; 127 return Buffer(_buffer + BufferSize * i, size); 128 } 129 } 130 throw std::runtime_error("BufferAllocator::Allocate: out of mapped memory"); 131 } 132 }; 133 134 }} 135 136 #endif 137