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