1 /* 2 * A page mapper providing memory pages to satisfy file accesses. 3 * 4 * Copyright (C) 2021 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 "ipc.h" 23 #include "page_mapper.h" 24 25 26 27 /* Provide mapped pages populated with the given 'accessor', with pages obtained 28 from the given 'pages' collection. */ 29 30 PageMapper::PageMapper(Accessor *accessor, Pages *pages) 31 : _accessor(accessor), _pages(pages) 32 { 33 } 34 35 /* Upon deallocation, purge active pages. */ 36 37 PageMapper::~PageMapper() 38 { 39 _map.purge(this, _pages); 40 } 41 42 /* Interface for the pager. */ 43 44 /* Return a flexpage providing access to the indicated file 'offset'. 45 46 The returned flexpage will either be an existing, compatible flexpage or a 47 completely new flexpage. 48 49 This method locks the mapper to prevent concurrent queries with the same 50 details, with the lock held until the queue operation releases the lock. */ 51 52 Flexpage *PageMapper::get(offset_t offset, map_flags_t flags) 53 { 54 _lock.lock(); 55 56 Flexpage *f = find(offset); 57 58 if (f == NULL) 59 f = flexpage(offset); 60 61 /* Record a new user of the flexpage and upgrade the access flags. */ 62 63 f->increment(); 64 f->upgrade(flags); 65 return f; 66 } 67 68 /* Queue the given 'flexpage' in the page collection, making it available for 69 eventual reuse. 70 71 This method unlocks the mapper. */ 72 73 void PageMapper::queue(Flexpage *flexpage) 74 { 75 _pages->queue(this, flexpage); 76 77 _lock.unlock(); 78 } 79 80 /* Flush pages in the given range from 'start' with 'size'. */ 81 82 void PageMapper::flush_all(offset_t start, offset_t size) 83 { 84 std::lock_guard<std::mutex> guard(_lock); 85 86 _map.flush_all(start, size, this, _pages); 87 } 88 89 /* Return the maximum extent of the mapped resource. */ 90 91 offset_t PageMapper::get_data_size() 92 { 93 return _accessor->get_size(); 94 } 95 96 /* Set the maximum extent of the mapped resource. */ 97 98 void PageMapper::set_data_size(offset_t size) 99 { 100 _accessor->set_size(size); 101 } 102 103 /* Internal flexpage retrieval methods. */ 104 105 /* Find an existing flexpage for 'offset'. Where the accessor has registered a 106 compatible flexpage, an attempt is made to reserve it in the page collection; 107 if this succeeds, the flexpage is returned. Otherwise, NULL is returned. */ 108 109 Flexpage *PageMapper::find(offset_t offset) 110 { 111 Flexpage *flexpage = _map.find(offset); 112 113 /* Between finding and reserving a flexpage, there is a possibility that 114 another accessor might acquire the flexpage, issue it, and even purge 115 it. */ 116 117 if ((flexpage != NULL) && _pages->reserve(this, flexpage)) 118 return flexpage; 119 else 120 return NULL; 121 } 122 123 /* Obtain a new flexpage for the file 'offset'. If the page collection is unable 124 to obtain a completely new flexpage, an existing flexpage is requested from 125 the page collection and recycled. 126 127 The obtained flexpage is filled with content. */ 128 129 Flexpage *PageMapper::flexpage(offset_t offset) 130 { 131 Flexpage *flexpage = _pages->flexpage(); 132 133 /* Obtain an existing flexpage and reuse it. */ 134 135 if (flexpage == NULL) 136 flexpage = _pages->remove(); 137 138 flexpage->reset(offset); 139 140 fill(flexpage); 141 _map.insert(flexpage); 142 return flexpage; 143 } 144 145 /* Interface for the page collection. */ 146 147 /* Remove the record of 'flexpage' in this accessor, flushing its content. */ 148 149 void PageMapper::remove(Flexpage *flexpage) 150 { 151 _map.remove(this, flexpage); 152 } 153 154 /* Data transfer methods. */ 155 156 void PageMapper::fill(Flexpage *flexpage) 157 { 158 _accessor->fill(flexpage); 159 } 160 161 void PageMapper::flush(Flexpage *flexpage, bool purge) 162 { 163 if (flexpage->decrement() || purge) 164 { 165 /* NOTE: Derived flexpages might potentially support their contents 166 being merged into the flushed data, although this is a 167 non-trivial problem. */ 168 169 if (flexpage->modified()) 170 _accessor->flush(flexpage); 171 172 /* Unmap the flexpage, requiring users of its memory to request another 173 flexpage in future. */ 174 175 ipc_unmap_flexpage(flexpage); 176 flexpage->invalidate(); 177 178 /* Unmap all derived flexpages, since these rely on the underlying file 179 contents. */ 180 181 DerivedFlexpages::iterator it; 182 183 for (it = flexpage->derived.begin(); it != flexpage->derived.end(); it++) 184 { 185 ipc_unmap_flexpage(*it); 186 (*it)->invalidate(); 187 } 188 189 flexpage->disassociate(); 190 } 191 } 192 193 // vim: tabstop=4 expandtab shiftwidth=4