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 void PipePaging::discard_region(unsigned int i) 99 { 100 PageMapper *mapper = _regions[i]; 101 102 if (mapper != NULL) 103 { 104 mapper->detach(); 105 _regions[i] = NULL; 106 delete mapper; 107 } 108 } 109 110 /* Detach one endpoint. */ 111 112 void PipePaging::detach() 113 { 114 if (!_endpoints) 115 return; 116 else 117 _endpoints--; 118 119 /* Return if the other endpoint is attached. */ 120 121 if (_endpoints) 122 return; 123 124 /* Discard all regions from the pipe. */ 125 126 for (unsigned int i = 0; i < 2; i++) 127 discard_region(i); 128 129 /* Release IRQs. */ 130 131 for (unsigned int i = 0; i < 2; i++) 132 { 133 if (l4_is_valid_cap(_irqs[i])) 134 { 135 ipc_cap_free_um(_irqs[i]); 136 _irqs[i] = L4_INVALID_CAP; 137 } 138 } 139 140 /* Delete the page collection and related objects. */ 141 142 delete _pages; 143 delete _queue; 144 delete _memory; 145 } 146 147 /* Add a region to the sequence. */ 148 149 PageMapper *PipePaging::add_region() 150 { 151 /* If the writer already accesses a different region to the reader, no new 152 region is added. */ 153 154 if (_writing != _reading) 155 return NULL; 156 157 /* Select the other region of the pair being maintained. */ 158 159 _writing = 1 - _writing; 160 161 /* Make a new mapper for the region. */ 162 163 PageMapper *mapper = new PageMapper(&_accessors[_writing], _pages); 164 165 /* Initialise and record the mapper. */ 166 167 mapper->attach(); 168 mapper->set_data_size(0); 169 170 _regions[_writing] = mapper; 171 172 /* Let the writer notify the reader. */ 173 174 notify(true); 175 176 /* Return the next region's mapper. */ 177 178 return mapper; 179 } 180 181 /* Return the current region for reading. */ 182 183 PageMapper *PipePaging::current_region() 184 { 185 return _regions[_reading]; 186 } 187 188 /* Return the next region for the reader if the writer is using a different one. 189 Otherwise, return NULL. */ 190 191 PageMapper *PipePaging::next_region() 192 { 193 /* If the reader already accesses the same region to the writer, no next 194 region can be obtained. */ 195 196 if (_reading == _writing) 197 return NULL; 198 199 /* Detach and discard the current page mapper. */ 200 201 discard_region(_reading); 202 203 /* Select the next region. */ 204 205 _reading = 1 - _reading; 206 207 /* Let the reader notify the writer. */ 208 209 notify(false); 210 211 /* Return the next region's mapper. */ 212 213 return _regions[_reading]; 214 } 215 216 // vim: tabstop=4 expandtab shiftwidth=4