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 PageMapper::PageMapper(Accessor *accessor, Pages *pages) 28 : _accessor(accessor), _pages(pages), _attached(0) 29 { 30 } 31 32 /* Accounting methods. */ 33 34 /* Attach a pager, opening the accessor if required. */ 35 36 void PageMapper::attach() 37 { 38 std::lock_guard<std::mutex> guard(_lock); 39 40 if (!_attached) 41 _accessor->open(); 42 43 _attached += 1; 44 } 45 46 /* Detach a pager, purging active pages and closing the accessor if no more 47 pagers are attached. Return whether any pagers are still attached. */ 48 49 unsigned int PageMapper::detach() 50 { 51 std::lock_guard<std::mutex> guard(_lock); 52 53 if (_attached) 54 { 55 _attached -= 1; 56 57 if (!_attached) 58 { 59 _map.purge(this, _pages); 60 _accessor->close(); 61 } 62 } 63 64 return _attached; 65 } 66 67 /* Interface for the pager. */ 68 69 /* Return a flexpage providing access to the indicated file 'offset'. 70 71 The returned flexpage will either be an existing, compatible flexpage or a 72 completely new flexpage. 73 74 This method locks the mapper to prevent concurrent queries with the same 75 details, with the lock held until the queue operation releases the lock. */ 76 77 Flexpage *PageMapper::get(offset_t offset, flags_t flags) 78 { 79 _lock.lock(); 80 81 Flexpage *f = find(offset); 82 83 if (f == NULL) 84 f = flexpage(offset); 85 86 /* Record a new user of the flexpage and upgrade the access flags. */ 87 88 f->increment(); 89 f->upgrade(flags); 90 return f; 91 } 92 93 /* Queue the given 'flexpage' in the page collection, making it available for 94 eventual reuse. 95 96 This method unlocks the mapper. */ 97 98 void PageMapper::queue(Flexpage *flexpage) 99 { 100 _pages->queue(this, flexpage); 101 102 _lock.unlock(); 103 } 104 105 /* Flush pages in the given range from 'start' with 'size'. */ 106 107 void PageMapper::flush_all(offset_t start, offset_t size) 108 { 109 std::lock_guard<std::mutex> guard(_lock); 110 111 _map.flush_all(start, size, this, _pages); 112 } 113 114 /* Return the maximum extent of the mapped resource. */ 115 116 offset_t PageMapper::get_data_size() 117 { 118 return _accessor->get_size(); 119 } 120 121 /* Set the maximum extent of the mapped resource. */ 122 123 void PageMapper::set_data_size(offset_t size) 124 { 125 _accessor->set_size(size); 126 } 127 128 /* Internal flexpage retrieval methods. */ 129 130 /* Find an existing flexpage for 'offset'. Where the accessor has registered a 131 compatible flexpage, an attempt is made to reserve it in the page collection; 132 if this succeeds, the flexpage is returned. Otherwise, NULL is returned. */ 133 134 Flexpage *PageMapper::find(offset_t offset) 135 { 136 Flexpage *flexpage = _map.find(offset); 137 138 /* Between finding and reserving a flexpage, there is a possibility that 139 another accessor might acquire the flexpage, issue it, and even purge 140 it. */ 141 142 if ((flexpage != NULL) && _pages->reserve(this, flexpage)) 143 return flexpage; 144 else 145 return NULL; 146 } 147 148 /* Obtain a new flexpage for the file 'offset'. If the page collection is unable 149 to obtain a completely new flexpage, an existing flexpage is requested from 150 the page collection and recycled. 151 152 The obtained flexpage is filled with content. */ 153 154 Flexpage *PageMapper::flexpage(offset_t offset) 155 { 156 Flexpage *flexpage = _pages->flexpage(); 157 158 /* Obtain an existing flexpage and reuse it. */ 159 160 if (flexpage == NULL) 161 flexpage = _pages->remove(); 162 163 flexpage->reset(offset); 164 165 fill(flexpage); 166 _map.insert(flexpage); 167 return flexpage; 168 } 169 170 /* Interface for the page collection. */ 171 172 /* Remove the record of 'flexpage' in this accessor, flushing its content. */ 173 174 void PageMapper::remove(Flexpage *flexpage) 175 { 176 _map.remove(this, flexpage); 177 } 178 179 /* Data transfer methods. */ 180 181 void PageMapper::fill(Flexpage *flexpage) 182 { 183 _accessor->fill(flexpage); 184 } 185 186 void PageMapper::flush(Flexpage *flexpage, bool purge) 187 { 188 if (flexpage->decrement() || purge) 189 { 190 _accessor->flush(flexpage); 191 ipc_unmap_flexpage(flexpage); 192 flexpage->invalidate(); 193 } 194 } 195 196 // vim: tabstop=4 expandtab shiftwidth=4