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 24 #include "notification.h" 25 #include "notifier_client.h" 26 27 #include "resource_server.h" 28 29 30 31 /* Initialise endpoints and flags for notifications. */ 32 33 NotificationSupport::NotificationSupport(unsigned int endpoints) 34 : _min_endpoints(endpoints) 35 { 36 _endpoints.resize(_min_endpoints); 37 } 38 39 NotificationSupport::~NotificationSupport() 40 { 41 } 42 43 /* Return an endpoint number, removing the mapping from the given notifier if 44 indicated. */ 45 46 long NotificationSupport::_get_endpoint(l4_cap_idx_t notifier, bool remove, unsigned int *endpoint) 47 { 48 NotifierEndpointMap::iterator itp = _subscribers.find(notifier); 49 50 if (itp == _subscribers.end()) 51 return -L4_ENOENT; 52 53 *endpoint = itp->second; 54 55 if (remove) 56 _subscribers.erase(itp); 57 58 return L4_EOK; 59 } 60 61 /* Subscribe to notifications using a notification object, returning the 62 endpoint number. */ 63 64 long NotificationSupport::subscribe(l4_cap_idx_t notifier, notify_flags_t flags) 65 { 66 std::lock_guard<std::mutex> guard(_lock); 67 68 unsigned int endpoint = _endpoints.size(); 69 _endpoints.resize(endpoint + 1); 70 71 /* Propagate deferred flags and values for new endpoints. */ 72 73 if (_deferred_flags) 74 { 75 _endpoints[endpoint].deferred_flags = _deferred_flags; 76 _endpoints[endpoint].deferred_values = _deferred_values; 77 } 78 79 return _subscribe(endpoint, notifier, flags); 80 } 81 82 /* Subscribe to a specific endpoint's notifications using a notification 83 object. */ 84 85 long NotificationSupport::subscribe(unsigned int endpoint, l4_cap_idx_t notifier, 86 notify_flags_t flags) 87 { 88 std::lock_guard<std::mutex> guard(_lock); 89 90 if (endpoint >= _endpoints.size()) 91 _endpoints.resize(endpoint + 1); 92 93 /* Propagate deferred flags for new endpoints. */ 94 95 if (_deferred_flags) 96 { 97 _endpoints[endpoint].deferred_flags = _deferred_flags; 98 _endpoints[endpoint].deferred_values = _deferred_values; 99 } 100 101 return _subscribe(endpoint, notifier, flags); 102 } 103 104 /* Unsubscribe from an endpoint's notifications. */ 105 106 void NotificationSupport::unsubscribe(l4_cap_idx_t notifier) 107 { 108 std::lock_guard<std::mutex> guard(_lock); 109 110 _unsubscribe(notifier); 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