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