1 /* 2 * Generic object event 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 <map> 23 #include <mutex> 24 25 #include <l4/sys/irq.h> 26 27 #include <ipc/cap_alloc.h> 28 #include <ipc/server.h> 29 #include <resource/resource_server.h> 30 31 #include "notification_client.h" 32 #include "notifier.h" 33 #include "notifier_server.h" 34 35 36 37 /* Null notification state. */ 38 39 static ObjectNotificationState _null_state; 40 41 42 43 /* Lock protecting per-task notifier access. */ 44 45 static std::mutex _lock; 46 47 /* Per-task storage for specific waiting operations. */ 48 49 static SpecificObjectNotifier *_notifier = NULL; 50 51 52 53 /* Return the per-task notifier for object-specific waiting operations. */ 54 55 SpecificObjectNotifier *notifier_get_task_notifier() 56 { 57 std::lock_guard<std::mutex> guard(_lock); 58 59 /* Start any new notifier. */ 60 61 if (_notifier == NULL) 62 { 63 _notifier = new SpecificObjectNotifier; 64 _notifier->start(); 65 } 66 67 return _notifier; 68 } 69 70 /* Return a local notifier for general object waiting operations. */ 71 72 GeneralObjectNotifier *notifier_get_local_notifier() 73 { 74 GeneralObjectNotifier *notifier = new GeneralObjectNotifier; 75 76 notifier->start(); 77 return notifier; 78 } 79 80 81 82 /* Virtual destructor required for base class instance reference deletion. */ 83 84 ObjectNotifier::~ObjectNotifier() 85 { 86 stop(); 87 88 ServerConfigs::iterator it; 89 90 for (it = _configs.begin(); it != _configs.end(); it++) 91 delete *it; 92 93 _configs.clear(); 94 95 /* Handle deletion of the special task notifier. */ 96 97 if (this == _notifier) 98 _notifier = NULL; 99 } 100 101 102 103 /* Start listening for notifications. */ 104 105 long ObjectNotifier::start() 106 { 107 if (_started) 108 return L4_EOK; 109 110 /* Create a new thread to serve a "null" resource. This resource is not used 111 for notifications but merely for control purposes. */ 112 113 NotifierResource *notifier = new NotifierResource; 114 ResourceServer server(notifier); 115 long err = server.start_thread(true, false); 116 117 if (err) 118 return err; 119 120 _configs.push_back(server.config()); 121 _started = true; 122 123 /* Retain the IRQ created for the server for control purposes. */ 124 125 _irq = server.config()->irq; 126 127 return L4_EOK; 128 } 129 130 131 132 /* Stop the notifier. */ 133 134 void ObjectNotifier::stop() 135 { 136 if (l4_is_valid_cap(_irq)) 137 { 138 l4_irq_trigger(_irq); 139 _irq = L4_INVALID_CAP; 140 } 141 } 142 143 144 145 /* Return notification state for the given object or null state if no record 146 existed for the object. */ 147 148 ObjectNotificationState &ObjectNotifier::object_state(notifiable_t *object, bool create) 149 { 150 ObjectNotificationStates::iterator it = _state.find(object); 151 152 if (it == _state.end()) 153 { 154 if (create) 155 return _state[object]; 156 else 157 return _null_state; 158 } 159 160 return it->second; 161 } 162 163 /* Subscribe to notification events on an object. */ 164 165 long ObjectNotifier::subscribe(notifiable_t *object, notify_flags_t flags) 166 { 167 /* Acquire the lock for state lookup. */ 168 169 std::unique_lock<std::mutex> state_guard(_state_lock); 170 171 /* Obtain potentially new state for the object. */ 172 173 ObjectNotificationState &state = object_state(object, true); 174 175 if (state.is_null()) 176 { 177 /* Serve the new object in the notifier thread. */ 178 179 NotifierResource *resource = new NotifierResource(this, object); 180 ResourceServer server(resource); 181 long err = server.start_in_thread(_configs.front()->thread); 182 183 if (err) 184 return err; 185 186 _configs.push_back(server.config()); 187 state.endpoint = server.config()->server; 188 } 189 190 /* Forbid repeated subscription. 191 NOTE: Could instead rely on being unsubscribed, releasing the existing 192 endpoint. */ 193 194 else 195 return -L4_EEXIST; 196 197 /* Allow this object to be re-entered. This may occur because the subscribe 198 operation can cause deferred notifications to be sent back to the 199 subscribed notifier and to this object. */ 200 201 state_guard.unlock(); 202 203 /* Subscribe, sending the notification endpoint via the principal reference 204 for the object. */ 205 206 client_Notification notify(object->base->ref); 207 208 return notify.subscribe(state.endpoint, flags); 209 } 210 211 /* Unsubscribe from notification events on an object. */ 212 213 long ObjectNotifier::unsubscribe(notifiable_t *object) 214 { 215 /* Acquire the lock for state lookup. */ 216 217 std::unique_lock<std::mutex> state_guard(_state_lock); 218 219 ObjectNotificationState &state = object_state(object, false); 220 221 if (state.is_null()) 222 return -L4_ENOENT; 223 224 /* Unsubscribe via the notification interface. */ 225 226 client_Notification notify(object->base->ref); 227 228 notify.unsubscribe(); 229 230 return remove_endpoint(object, state.endpoint); 231 } 232 233 /* Remove a notification endpoint for an object. */ 234 235 long ObjectNotifier::remove_endpoint(notifiable_t *object, l4_cap_idx_t endpoint) 236 { 237 if (l4_is_invalid_cap(endpoint)) 238 return -L4_EINVAL; 239 240 ipc_cap_free_um(endpoint); 241 242 _state.erase(object); 243 244 /* Remove the lock for updating object state. */ 245 246 _object_locks.erase(object); 247 248 return L4_EOK; 249 } 250 251 252 253 /* Handle a notification event for an object. */ 254 255 void GeneralObjectNotifier::notify(notifiable_t *object, notify_flags_t flags, 256 notify_values_t values) 257 { 258 /* Enter critical section for the notifier (affecting all objects). */ 259 260 std::unique_lock<std::mutex> general_guard(_general_lock); 261 262 /* Acquire the lock for state lookup. */ 263 264 std::unique_lock<std::mutex> state_guard(_state_lock); 265 266 ObjectNotificationState &state = object_state(object, false); 267 268 if (state.is_null()) 269 return; 270 271 /* Acquire the lock for the object state itself. */ 272 273 std::unique_lock<std::mutex> object_guard(state.lock); 274 275 /* Record flags and note previous flags. */ 276 277 notify_flags_t recorded = state.pending_notifications; 278 279 state.pending_notifications |= flags; 280 state.pending_values = values; 281 282 /* Add an object queue entry for any objects without previous notifications. */ 283 284 if (!recorded) 285 _affected.push_back(object); 286 287 /* Notify any waiting caller. */ 288 289 _general_condition.notify_one(); 290 } 291 292 void SpecificObjectNotifier::notify(notifiable_t *object, notify_flags_t flags, 293 notify_values_t values) 294 { 295 /* Acquire the lock for state lookup. */ 296 297 std::unique_lock<std::mutex> state_guard(_state_lock); 298 299 ObjectNotificationState &state = object_state(object, false); 300 301 if (state.is_null()) 302 return; 303 304 /* Acquire the lock for the object state itself. */ 305 306 std::unique_lock<std::mutex> object_guard(state.lock); 307 308 state.pending_notifications |= flags; 309 state.pending_values = values; 310 311 /* Notify any waiting caller. */ 312 313 state.condition.notify_one(); 314 } 315 316 317 318 /* Transfer pending notifications to the given object. This must be called with 319 a lock acquired on the object notification state. */ 320 321 bool ObjectNotifier::_transfer(ObjectNotificationState &state, notifiable_t *object) 322 { 323 notify_flags_t recorded = state.pending_notifications; 324 325 if (recorded) 326 { 327 object->notifications = recorded; 328 object->values = state.pending_values; 329 state.pending_notifications = 0; 330 return true; 331 } 332 333 return false; 334 } 335 336 337 338 /* Obtain object state and transfer notifications. */ 339 340 bool GeneralObjectNotifier::_retrieve_for_object(notifiable_t *object) 341 { 342 /* Acquire the lock for state lookup. */ 343 344 std::unique_lock<std::mutex> state_guard(_state_lock); 345 346 ObjectNotificationState &state = object_state(object, false); 347 348 if (state.is_null()) 349 return false; 350 351 /* Acquire the lock for the object state itself, then release the state lock. */ 352 353 std::unique_lock<std::mutex> object_guard(state.lock); 354 355 state_guard.unlock(); 356 357 /* Call generic method to transfer notifications, if possible. */ 358 359 return _transfer(state, object); 360 } 361 362 /* Obtain queued objects until one is found that still has events recorded for 363 it. This must be called with the notifier's general lock acquired. */ 364 365 bool GeneralObjectNotifier::_retrieve(notifiable_t **object) 366 { 367 while (!_affected.empty()) 368 { 369 *object = _affected.front(); 370 _affected.pop_front(); 371 372 if (_retrieve_for_object(*object)) 373 return true; 374 } 375 376 return false; 377 } 378 379 380 381 /* Wait for notification events on objects. */ 382 383 long GeneralObjectNotifier::wait(notifiable_t **object) 384 { 385 std::unique_lock<std::mutex> general_guard(_general_lock); 386 387 while (1) 388 { 389 /* With pending notifications, update the first object and exit. */ 390 391 if (_retrieve(object)) 392 break; 393 394 /* Otherwise, wait for notifications. */ 395 396 _general_condition.wait(general_guard); 397 } 398 399 return L4_EOK; 400 } 401 402 /* Wait for notifications from a single object. */ 403 404 long SpecificObjectNotifier::wait_object(notifiable_t *object) 405 { 406 /* Acquire the lock for reading object state. */ 407 408 std::unique_lock<std::mutex> state_guard(_state_lock); 409 410 ObjectNotificationState &state = object_state(object, false); 411 412 if (state.is_null()) 413 return -L4_EINVAL; 414 415 /* Acquire the lock for the object state itself, then release the state lock. */ 416 417 std::unique_lock<std::mutex> object_guard(state.lock); 418 419 state_guard.unlock(); 420 421 while (1) 422 { 423 /* With pending notifications, update the object and exit. */ 424 425 if (_transfer(state, object)) 426 break; 427 428 /* Otherwise, wait for notifications. */ 429 430 state.condition.wait(object_guard); 431 } 432 433 return L4_EOK; 434 } 435 436 437 438 /* Object-specific resource methods. */ 439 440 ipc_server_default_config_type NotifierResource::config() 441 { 442 return config_Notifier; 443 } 444 445 /* Register a notification received by an object-specific resource. */ 446 447 long NotifierResource::notify(notify_flags_t flags, notify_values_t values) 448 { 449 if (_notifier != NULL) 450 _notifier->notify(_object, flags, values); 451 452 return L4_EOK; 453 } 454 455 // vim: tabstop=2 expandtab shiftwidth=2