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 <ipc/cap_alloc.h> 23 #include <mem/memory_preallocated.h> 24 25 #include "page_queue_partitioned.h" 26 #include "pipe_paging.h" 27 28 #include "notifier_client.h" 29 30 31 32 PipePaging::PipePaging(Memory *memory, offset_t size) 33 : _memory(NULL), _size(size) 34 { 35 /* Reserve space for two pipe regions. */ 36 37 _memory = new MemoryPreallocated(memory, round(size, memory->region_size()) * 2); 38 _queue = new PageQueuePartitioned(); 39 _pages = new Pages(_memory, _queue); 40 41 /* Reset the mappers associated with the regions. */ 42 43 for (unsigned int i = 0; i < 2; i++) 44 _regions[i] = NULL; 45 46 /* Initialise endpoints and flags for notifications. */ 47 48 for (unsigned int i = 0; i < 2; i++) 49 { 50 _flags[i] = 0; 51 _deferred[i] = 0; 52 } 53 } 54 55 /* Subscribe to an endpoint's notifications using a notification endpoint. */ 56 57 void PipePaging::subscribe(bool writing, l4_cap_idx_t notifier, notify_flags_t flags) 58 { 59 int i = writing ? 1 : 0; 60 61 _notifiers[i].insert(notifier); 62 _flags[i] = flags; 63 64 /* Send deferred conditions on behalf of the other endpoint held before 65 subscription occurred. */ 66 67 if (_deferred[i]) 68 { 69 notify(!writing, _deferred[i]); 70 _deferred[i] = 0; 71 } 72 } 73 74 /* Unsubscribe from an endpoint's notifications. */ 75 76 void PipePaging::unsubscribe(bool writing, l4_cap_idx_t notifier) 77 { 78 int i = writing ? 1 : 0; 79 std::set<l4_cap_idx_t>::iterator it = _notifiers[i].find(notifier); 80 81 if (it != _notifiers[i].end()) 82 { 83 _notifiers[i].erase(it); 84 _flags[i] = 0; 85 _deferred[i] = 0; 86 ipc_cap_free_um(notifier); 87 } 88 } 89 90 /* Notify the other endpoint. */ 91 92 void PipePaging::notify(bool writing, notify_flags_t flags) 93 { 94 /* Let the writer notify the reader, and the other way round. */ 95 96 int i = writing ? 0 : 1; 97 98 /* Notify the other endpoint or hold any notification for potential future 99 subscription. */ 100 101 if (!_notifiers[i].empty()) 102 { 103 if (flags & _flags[i]) 104 { 105 std::set<l4_cap_idx_t>::iterator it; 106 107 for (it = _notifiers[i].begin(); it != _notifiers[i].end(); it++) 108 { 109 client_Notifier notifier(*it); 110 111 notifier.notify(flags & _flags[i]); 112 } 113 } 114 } 115 else 116 _deferred[i] = flags; 117 } 118 119 /* Return whether one or more endpoints have detached. */ 120 121 int PipePaging::closed() 122 { 123 return _endpoints < 2; 124 } 125 126 void PipePaging::discard_region(unsigned int i) 127 { 128 PageMapper *mapper = _regions[i]; 129 130 if (mapper != NULL) 131 { 132 mapper->detach(); 133 _regions[i] = NULL; 134 delete mapper; 135 } 136 } 137 138 /* Detach one endpoint. */ 139 140 void PipePaging::detach() 141 { 142 if (!_endpoints) 143 return; 144 else 145 _endpoints--; 146 147 /* Return if the other endpoint is attached. */ 148 149 if (_endpoints) 150 return; 151 152 /* Discard all regions from the pipe. */ 153 154 for (unsigned int i = 0; i < 2; i++) 155 discard_region(i); 156 157 /* Release notifiers. */ 158 159 for (unsigned int i = 0; i < 2; i++) 160 { 161 std::set<l4_cap_idx_t>::iterator it; 162 163 for (it = _notifiers[i].begin(); it != _notifiers[i].end(); it++) 164 ipc_cap_free_um(*it); 165 166 _notifiers[i].clear(); 167 } 168 169 /* Delete the page collection and related objects. */ 170 171 delete _pages; 172 delete _queue; 173 delete _memory; 174 } 175 176 /* Add a region to the sequence. */ 177 178 PageMapper *PipePaging::add_region() 179 { 180 /* If the writer already accesses a different region to the reader, no new 181 region is added. */ 182 183 if (_writing != _reading) 184 return NULL; 185 186 /* Select the other region of the pair being maintained. */ 187 188 _writing = 1 - _writing; 189 190 /* Make a new mapper for the region. */ 191 192 PageMapper *mapper = new PageMapper(&_accessors[_writing], _pages); 193 194 /* Initialise and record the mapper. */ 195 196 mapper->attach(); 197 mapper->set_data_size(0); 198 199 _regions[_writing] = mapper; 200 201 /* Let the writer notify the reader. */ 202 203 notify(true, NOTIFY_CONTENT_AVAILABLE); 204 205 /* Return the next region's mapper. */ 206 207 return mapper; 208 } 209 210 /* Return the current region for reading. */ 211 212 PageMapper *PipePaging::current_region() 213 { 214 return _regions[_reading]; 215 } 216 217 /* Return the next region for the reader if the writer is using a different one. 218 Otherwise, return NULL. */ 219 220 PageMapper *PipePaging::next_region() 221 { 222 /* If the reader already accesses the same region to the writer, no next 223 region can be obtained. */ 224 225 if (_reading == _writing) 226 return NULL; 227 228 /* Detach and discard the current page mapper. */ 229 230 discard_region(_reading); 231 232 /* Select the next region. */ 233 234 _reading = 1 - _reading; 235 236 /* Let the reader notify the writer. */ 237 238 notify(false, NOTIFY_SPACE_AVAILABLE); 239 240 /* Return the next region's mapper. */ 241 242 return _regions[_reading]; 243 } 244 245 // vim: tabstop=4 expandtab shiftwidth=4