1 /* 2 * A page mapper providing memory pages to satisfy file accesses. 3 * 4 * Copyright (C) 2021, 2022, 2023 Paul Boddie <paul@boddie.org.uk> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License as 8 * published by the Free Software Foundation; either version 2 of 9 * the License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA 20 */ 21 22 #include "page_mapper.h" 23 24 25 26 /* Provide mapped pages populated with the given 'accessor', with pages obtained 27 from the given 'pages' collection. */ 28 29 PageMapper::PageMapper(Accessor *accessor, Pages *pages) 30 : _accessor(accessor), _pages(pages) 31 { 32 } 33 34 /* Upon deallocation, purge active pages. */ 35 36 PageMapper::~PageMapper() 37 { 38 _map.purge(this, _pages); 39 } 40 41 /* Interface for the pager. */ 42 43 /* Return a flexpage providing access to the indicated file 'offset'. 44 45 The returned flexpage will either be an existing, compatible flexpage or a 46 completely new flexpage. 47 48 This method locks the mapper to prevent concurrent queries with the same 49 details, with the lock held until the queue operation releases the lock. */ 50 51 Flexpage *PageMapper::get(offset_t offset, map_flags_t flags) 52 { 53 _lock.lock(); 54 55 Flexpage *f = find(offset); 56 57 if (f == NULL) 58 f = flexpage(offset); 59 60 /* Record a new user of the flexpage and upgrade the access flags. */ 61 62 f->increment(); 63 f->upgrade(flags); 64 return f; 65 } 66 67 /* Queue the given 'flexpage' in the page collection, making it available for 68 eventual reuse. 69 70 This method unlocks the mapper. */ 71 72 void PageMapper::queue(Flexpage *flexpage) 73 { 74 _pages->queue(this, flexpage); 75 76 _lock.unlock(); 77 } 78 79 /* Flush pages in the given range from 'start' with 'size'. */ 80 81 void PageMapper::flush_all(offset_t start, offset_t size) 82 { 83 std::lock_guard<std::mutex> guard(_lock); 84 85 _map.flush_all(start, size, this, _pages); 86 } 87 88 /* Return the maximum extent of the mapped resource. */ 89 90 offset_t PageMapper::get_data_size() 91 { 92 return _accessor->get_size(); 93 } 94 95 /* Set the maximum extent of the mapped resource. */ 96 97 void PageMapper::set_data_size(offset_t size) 98 { 99 _accessor->set_size(size); 100 } 101 102 /* Internal flexpage retrieval methods. */ 103 104 /* Find an existing flexpage for 'offset'. Where the accessor has registered a 105 compatible flexpage, an attempt is made to reserve it in the page collection; 106 if this succeeds, the flexpage is returned. Otherwise, NULL is returned. */ 107 108 Flexpage *PageMapper::find(offset_t offset) 109 { 110 Flexpage *flexpage = _map.find(offset); 111 112 /* Between finding and reserving a flexpage, there is a possibility that 113 another accessor might acquire the flexpage, issue it, and even purge 114 it. */ 115 116 if ((flexpage != NULL) && _pages->reserve(this, flexpage)) 117 return flexpage; 118 else 119 return NULL; 120 } 121 122 /* Obtain a new flexpage for the file 'offset'. If the page collection is unable 123 to obtain a completely new flexpage, an existing flexpage is requested from 124 the page collection and recycled. 125 126 The obtained flexpage is filled with content. */ 127 128 Flexpage *PageMapper::flexpage(offset_t offset) 129 { 130 Flexpage *flexpage = _pages->flexpage(); 131 132 flexpage->reset(offset); 133 134 fill(flexpage); 135 _map.insert(flexpage); 136 return flexpage; 137 } 138 139 /* Interface for the page collection. */ 140 141 /* Remove the record of 'flexpage' in this accessor, flushing its content. */ 142 143 void PageMapper::remove(Flexpage *flexpage) 144 { 145 _map.remove(this, flexpage); 146 } 147 148 /* Data transfer methods. */ 149 150 void PageMapper::fill(Flexpage *flexpage) 151 { 152 _accessor->fill(flexpage); 153 } 154 155 void PageMapper::flush(Flexpage *flexpage, bool purge) 156 { 157 if (flexpage->decrement() || purge) 158 { 159 /* NOTE: Derived flexpages might potentially support their contents 160 being merged into the flushed data, although this is a 161 non-trivial problem. */ 162 163 if (flexpage->modified()) 164 _accessor->flush(flexpage); 165 166 /* Unmap the flexpage, requiring users of its memory to request another 167 flexpage in future. This also unmaps all derived flexpages, since 168 these rely on the underlying file contents. */ 169 170 flexpage->invalidate(); 171 } 172 } 173 174 // vim: tabstop=4 expandtab shiftwidth=4