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 long NotificationSupport::unsubscribe(l4_cap_idx_t notifier) 107 { 108 std::lock_guard<std::mutex> guard(_lock); 109 110 _unsubscribe(notifier); 111 return L4_EOK; 112 } 113 114 /* Common subscription functionality. */ 115 116 long NotificationSupport::_subscribe(unsigned int endpoint, l4_cap_idx_t notifier, 117 notify_flags_t flags) 118 { 119 /* Record details of the notifier itself. */ 120 121 NotificationEndpoint &ep = _endpoints[endpoint]; 122 123 ep.notifiers.insert(notifier); 124 ep.flags = flags; 125 126 /* Send deferred conditions held from before subscription occurred. */ 127 128 if (ep.deferred_flags) 129 { 130 _notify(endpoint, ep.deferred_flags, ep.deferred_values); 131 ep.deferred_flags = 0; 132 ep.deferred_values = NOTIFY_VALUES_NULL; 133 } 134 135 /* Record the notifier's endpoint. */ 136 137 _subscribers[notifier] = endpoint; 138 return L4_EOK; 139 } 140 141 void NotificationSupport::_unsubscribe(l4_cap_idx_t notifier) 142 { 143 /* Find and remove the notifier endpoint record. */ 144 145 unsigned int endpoint; 146 long err = _get_endpoint(notifier, true, &endpoint); 147 148 if (err) 149 return; 150 151 /* Release the notifier. */ 152 153 NotificationEndpoint &ep = _endpoints[endpoint]; 154 NotifierSet::iterator it = ep.notifiers.find(notifier); 155 156 if (it != ep.notifiers.end()) 157 { 158 ep.notifiers.erase(it); 159 ipc_cap_free_um(notifier); 160 161 if (ep.notifiers.empty()) 162 { 163 ep.flags = 0; 164 ep.deferred_flags = 0; 165 ep.deferred_values = NOTIFY_VALUES_NULL; 166 } 167 } 168 } 169 170 /* Notify a particular endpoint. */ 171 172 void NotificationSupport::notify(unsigned int endpoint, notify_flags_t flags, 173 notify_values_t values) 174 { 175 std::lock_guard<std::mutex> guard(_lock); 176 177 _notify(endpoint, flags, values); 178 } 179 180 void NotificationSupport::_notify(unsigned int endpoint, notify_flags_t flags, 181 notify_values_t values) 182 { 183 if (endpoint >= _endpoints.size()) 184 return; 185 186 NotificationEndpoint &ep = _endpoints[endpoint]; 187 188 /* Notify the endpoint or hold any notification for potential future 189 subscription. */ 190 191 if (!ep.notifiers.empty()) 192 { 193 if (flags & ep.flags) 194 { 195 NotifierSet::iterator it; 196 197 for (it = ep.notifiers.begin(); it != ep.notifiers.end(); it++) 198 { 199 client_Notifier notifier(*it); 200 201 notifier.notify(flags & ep.flags, values); 202 } 203 } 204 } 205 else 206 { 207 ep.deferred_flags = flags; 208 ep.deferred_values = values; 209 } 210 } 211 212 /* Notify all endpoints. */ 213 214 void NotificationSupport::notify_all(notify_flags_t flags, notify_values_t values) 215 { 216 std::lock_guard<std::mutex> guard(_lock); 217 218 for (unsigned int i = 0; i < _endpoints.size(); i++) 219 _notify(i, flags, values); 220 221 /* Accumulate flags, but only record the most recent values. */ 222 223 _deferred_flags |= flags; 224 _deferred_values = values; 225 } 226 227 /* Notify the other endpoints. */ 228 229 void NotificationSupport::notify_others(unsigned int endpoint, notify_flags_t flags, 230 notify_values_t values) 231 { 232 std::lock_guard<std::mutex> guard(_lock); 233 234 _notify_others(endpoint, flags, values); 235 } 236 237 /* Notify the other endpoints given a notifier. */ 238 239 void NotificationSupport::notify_others(l4_cap_idx_t notifier, notify_flags_t flags, 240 notify_values_t values) 241 { 242 std::lock_guard<std::mutex> guard(_lock); 243 244 unsigned int endpoint; 245 long err = _get_endpoint(notifier, false, &endpoint); 246 247 if (err) 248 return; 249 250 _notify_others(endpoint, flags, values); 251 } 252 253 void NotificationSupport::_notify_others(unsigned int endpoint, 254 notify_flags_t flags, 255 notify_values_t values) 256 { 257 for (unsigned int i = 0; i < _endpoints.size(); i++) 258 if (i != endpoint) 259 _notify(i, flags, values); 260 261 /* Accumulate flags, but only record the most recent values. */ 262 263 _deferred_flags |= flags; 264 _deferred_values = values; 265 } 266 267 /* Release notifiers for each endpoint. */ 268 269 void NotificationSupport::release_notifiers() 270 { 271 std::lock_guard<std::mutex> guard(_lock); 272 273 for (unsigned int endpoint = 0; endpoint < _endpoints.size(); endpoint++) 274 { 275 NotificationEndpoint &ep = _endpoints[endpoint]; 276 NotifierSet::iterator it; 277 278 for (it = ep.notifiers.begin(); it != ep.notifiers.end(); it++) 279 ipc_cap_free_um(*it); 280 281 ep.notifiers.clear(); 282 ep.flags = 0; 283 ep.deferred_flags = 0; 284 ep.deferred_values = NOTIFY_VALUES_NULL; 285 } 286 } 287 288 // vim: tabstop=4 expandtab shiftwidth=4