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