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