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