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