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