1 /* 2 * A pipe paging coordinator, permitting memory sharing pipe endpoints. 3 * 4 * Copyright (C) 2021, 2022 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 <algorithm> 23 24 #include <mem/memory_preallocated.h> 25 26 #include "page_queue_partitioned.h" 27 #include "pipe_paging.h" 28 29 30 31 PipePaging::PipePaging(Memory *memory, offset_t size) 32 : NotificationSupport(2), _memory(NULL) 33 { 34 /* Employ a region size compatible with the configured page size. */ 35 36 _size = std::max<offset_t>(size, PAGE_SIZE); 37 38 /* Reserve space for two pipe regions. */ 39 40 _memory = new MemoryPreallocated(memory, round(size, memory->region_size()) * 2); 41 _queue = new PageQueuePartitioned(); 42 _pages = new Pages(_memory, _queue); 43 44 /* Reset the mappers associated with the regions. */ 45 46 for (unsigned int i = 0; i < 2; i++) 47 _regions[i] = NULL; 48 49 /* Initialise a region accessible by reader and writer. */ 50 51 _add_region(); 52 _next_region(); 53 } 54 55 PipePaging::~PipePaging() 56 { 57 /* Discard all regions from the pipe. */ 58 59 for (unsigned int i = 0; i < 2; i++) 60 discard_region(i); 61 62 /* Release notifiers. */ 63 64 release_notifiers(); 65 66 /* Delete the page collection and related objects. */ 67 68 delete _pages; 69 delete _queue; 70 delete _memory; 71 } 72 73 /* Return whether one or more endpoints have detached. */ 74 75 int PipePaging::closed() 76 { 77 return _active_endpoints < 2; 78 } 79 80 /* Detach one endpoint, returning the number still active. */ 81 82 unsigned int PipePaging::detach() 83 { 84 if (_active_endpoints) 85 _active_endpoints--; 86 87 return _active_endpoints; 88 } 89 90 /* Discard a region. */ 91 92 void PipePaging::discard_region(unsigned int i) 93 { 94 PageMapper *mapper = _regions[i]; 95 96 if (mapper != NULL) 97 { 98 _regions[i] = NULL; 99 delete mapper; 100 } 101 } 102 103 /* Add a region to the sequence. */ 104 105 PageMapper *PipePaging::_add_region() 106 { 107 /* If the writer already accesses a different region to the reader, no new 108 region is added. */ 109 110 if (_writing != _reading) 111 return NULL; 112 113 /* Select the other region of the pair being maintained. */ 114 115 _writing = 1 - _writing; 116 117 /* Make a new mapper for the region. */ 118 119 PageMapper *mapper = new PageMapper(&_accessors[_writing], _pages); 120 121 /* Initialise and record the mapper. */ 122 123 mapper->set_data_size(0); 124 125 _regions[_writing] = mapper; 126 127 /* Return the next region's mapper. */ 128 129 return mapper; 130 } 131 132 PageMapper *PipePaging::add_region() 133 { 134 PageMapper *mapper = _add_region(); 135 136 /* Let the writer notify the reader. */ 137 138 notify_others(PipePaging::WRITER, NOTIFY_CONTENT_AVAILABLE); 139 140 /* Return the next region's mapper. */ 141 142 return mapper; 143 } 144 145 /* Return the current region for reading. */ 146 147 PageMapper *PipePaging::current_region() 148 { 149 return _regions[_reading]; 150 } 151 152 /* Return the current region for reading or writing. */ 153 154 PageMapper *PipePaging::current_region(bool writing) 155 { 156 return _regions[writing ? _writing : _reading]; 157 } 158 159 /* Return the next region for the reader if the writer is using a different one. 160 Otherwise, return NULL. */ 161 162 PageMapper *PipePaging::_next_region() 163 { 164 /* If the reader already accesses the same region as the writer, no next 165 region can be obtained. */ 166 167 if (_reading == _writing) 168 return NULL; 169 170 /* Detach and discard the current page mapper. */ 171 172 discard_region(_reading); 173 174 /* Select the next region. */ 175 176 _reading = 1 - _reading; 177 178 /* Return the next region's mapper. */ 179 180 return _regions[_reading]; 181 } 182 183 PageMapper *PipePaging::next_region() 184 { 185 PageMapper *mapper = _next_region(); 186 187 /* Let the reader notify the writer. */ 188 189 notify_others(PipePaging::READER, NOTIFY_SPACE_AVAILABLE); 190 191 return mapper; 192 } 193 194 // vim: tabstop=4 expandtab shiftwidth=4