1 /* 2 * Notification support. 3 * 4 * Copyright (C) 2021, 2022, 2023 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 <resource/resource_server.h> 24 25 #include "notification.h" 26 #include "notifier_client.h" 27 28 29 30 /* Initialise endpoints and flags for notifications. */ 31 32 NotificationSupport::NotificationSupport(unsigned int endpoints) 33 : _min_endpoints(endpoints) 34 { 35 _endpoints.resize(_min_endpoints); 36 } 37 38 NotificationSupport::~NotificationSupport() 39 { 40 } 41 42 /* Return an endpoint number, removing the mapping from the given notifier if 43 indicated. */ 44 45 long NotificationSupport::_get_endpoint(l4_cap_idx_t notifier, bool remove, unsigned int *endpoint) 46 { 47 NotifierEndpointMap::iterator itp = _subscribers.find(notifier); 48 49 if (itp == _subscribers.end()) 50 return -L4_ENOENT; 51 52 *endpoint = itp->second; 53 54 if (remove) 55 _subscribers.erase(itp); 56 57 return L4_EOK; 58 } 59 60 /* Subscribe to notifications using a notification object, returning the 61 endpoint number. */ 62 63 long NotificationSupport::subscribe(l4_cap_idx_t notifier, notify_flags_t flags) 64 { 65 std::lock_guard<std::mutex> guard(_lock); 66 67 unsigned int endpoint = _endpoints.size(); 68 _endpoints.resize(endpoint + 1); 69 70 /* Propagate deferred flags and values for new endpoints. */ 71 72 if (_deferred_flags) 73 { 74 _endpoints[endpoint].deferred_flags = _deferred_flags; 75 _endpoints[endpoint].deferred_values = _deferred_values; 76 } 77 78 return _subscribe(endpoint, notifier, flags); 79 } 80 81 /* Subscribe to a specific endpoint's notifications using a notification 82 object. */ 83 84 long NotificationSupport::subscribe(unsigned int endpoint, l4_cap_idx_t notifier, 85 notify_flags_t flags) 86 { 87 std::lock_guard<std::mutex> guard(_lock); 88 89 if (endpoint >= _endpoints.size()) 90 _endpoints.resize(endpoint + 1); 91 92 /* Propagate deferred flags for new endpoints. */ 93 94 if (_deferred_flags) 95 { 96 _endpoints[endpoint].deferred_flags = _deferred_flags; 97 _endpoints[endpoint].deferred_values = _deferred_values; 98 } 99 100 return _subscribe(endpoint, notifier, flags); 101 } 102 103 /* Unsubscribe from an endpoint's notifications. */ 104 105 long NotificationSupport::unsubscribe(l4_cap_idx_t notifier) 106 { 107 std::lock_guard<std::mutex> guard(_lock); 108 109 _unsubscribe(notifier); 110 return L4_EOK; 111 } 112 113 /* Common subscription functionality. */ 114 115 long NotificationSupport::_subscribe(unsigned int endpoint, l4_cap_idx_t notifier, 116 notify_flags_t flags) 117 { 118 /* Record details of the notifier itself. */ 119 120 NotificationEndpoint &ep = _endpoints[endpoint]; 121 122 ep.notifiers.insert(notifier); 123 ep.flags = flags; 124 125 /* Send deferred conditions held from before subscription occurred. */ 126 127 if (ep.deferred_flags) 128 { 129 _notify(endpoint, ep.deferred_flags, ep.deferred_values); 130 ep.deferred_flags = 0; 131 ep.deferred_values = NOTIFY_VALUES_NULL; 132 } 133 134 /* Record the notifier's endpoint. */ 135 136 _subscribers[notifier] = endpoint; 137 return L4_EOK; 138 } 139 140 void NotificationSupport::_unsubscribe(l4_cap_idx_t notifier) 141 { 142 /* Find and remove the notifier endpoint record. */ 143 144 unsigned int endpoint; 145 long err = _get_endpoint(notifier, true, &endpoint); 146 147 if (err) 148 return; 149 150 /* Release the notifier. */ 151 152 NotificationEndpoint &ep = _endpoints[endpoint]; 153 NotifierSet::iterator it = ep.notifiers.find(notifier); 154 155 if (it != ep.notifiers.end()) 156 { 157 ep.notifiers.erase(it); 158 ipc_cap_free_um(notifier); 159 160 if (ep.notifiers.empty()) 161 { 162 ep.flags = 0; 163 ep.deferred_flags = 0; 164 ep.deferred_values = NOTIFY_VALUES_NULL; 165 } 166 } 167 } 168 169 /* Notify a particular endpoint. */ 170 171 void NotificationSupport::notify(unsigned int endpoint, notify_flags_t flags, 172 notify_values_t values) 173 { 174 std::lock_guard<std::mutex> guard(_lock); 175 176 _notify(endpoint, flags, values); 177 } 178 179 void NotificationSupport::_notify(unsigned int endpoint, notify_flags_t flags, 180 notify_values_t values) 181 { 182 if (endpoint >= _endpoints.size()) 183 return; 184 185 NotificationEndpoint &ep = _endpoints[endpoint]; 186 187 /* Notify the endpoint or hold any notification for potential future 188 subscription. */ 189 190 if (!ep.notifiers.empty()) 191 { 192 if (flags & ep.flags) 193 { 194 NotifierSet::iterator it; 195 196 for (it = ep.notifiers.begin(); it != ep.notifiers.end(); it++) 197 { 198 client_Notifier notifier(*it); 199 200 notifier.notify(flags & ep.flags, values); 201 } 202 } 203 } 204 else 205 { 206 ep.deferred_flags = flags; 207 ep.deferred_values = values; 208 } 209 } 210 211 /* Notify all endpoints. */ 212 213 void NotificationSupport::notify_all(notify_flags_t flags, notify_values_t values) 214 { 215 std::lock_guard<std::mutex> guard(_lock); 216 217 for (unsigned int i = 0; i < _endpoints.size(); i++) 218 _notify(i, flags, values); 219 220 /* Accumulate flags, but only record the most recent values. */ 221 222 _deferred_flags |= flags; 223 _deferred_values = values; 224 } 225 226 /* Notify the other endpoints. */ 227 228 void NotificationSupport::notify_others(unsigned int endpoint, notify_flags_t flags, 229 notify_values_t values) 230 { 231 std::lock_guard<std::mutex> guard(_lock); 232 233 _notify_others(endpoint, flags, values); 234 } 235 236 /* Notify the other endpoints given a notifier. */ 237 238 void NotificationSupport::notify_others(l4_cap_idx_t notifier, notify_flags_t flags, 239 notify_values_t values) 240 { 241 std::lock_guard<std::mutex> guard(_lock); 242 243 unsigned int endpoint; 244 long err = _get_endpoint(notifier, false, &endpoint); 245 246 if (err) 247 return; 248 249 _notify_others(endpoint, flags, values); 250 } 251 252 void NotificationSupport::_notify_others(unsigned int endpoint, 253 notify_flags_t flags, 254 notify_values_t values) 255 { 256 for (unsigned int i = 0; i < _endpoints.size(); i++) 257 if (i != endpoint) 258 _notify(i, flags, values); 259 260 /* Accumulate flags, but only record the most recent values. */ 261 262 _deferred_flags |= flags; 263 _deferred_values = values; 264 } 265 266 /* Release notifiers for each endpoint. */ 267 268 void NotificationSupport::release_notifiers() 269 { 270 std::lock_guard<std::mutex> guard(_lock); 271 272 for (unsigned int endpoint = 0; endpoint < _endpoints.size(); endpoint++) 273 { 274 NotificationEndpoint &ep = _endpoints[endpoint]; 275 NotifierSet::iterator it; 276 277 for (it = ep.notifiers.begin(); it != ep.notifiers.end(); it++) 278 ipc_cap_free_um(*it); 279 280 ep.notifiers.clear(); 281 ep.flags = 0; 282 ep.deferred_flags = 0; 283 ep.deferred_values = NOTIFY_VALUES_NULL; 284 } 285 } 286 287 // vim: tabstop=4 expandtab shiftwidth=4