1 // Copyright 2017 The Fuchsia Authors 2 // 3 // Use of this source code is governed by a MIT-style 4 // license that can be found in the LICENSE file or at 5 // https://opensource.org/licenses/MIT 6 7 #pragma once 8 9 #include <object/dispatcher.h> 10 #include <object/semaphore.h> 11 #include <object/state_observer.h> 12 13 #include <zircon/rights.h> 14 #include <zircon/syscalls/port.h> 15 #include <zircon/types.h> 16 17 #include <fbl/canary.h> 18 #include <fbl/intrusive_double_list.h> 19 #include <fbl/mutex.h> 20 #include <ktl/unique_ptr.h> 21 #include <kernel/spinlock.h> 22 23 #include <sys/types.h> 24 25 // Important pointers diagram for PortObserver 26 // 27 // The diagrams below show the *relevant* pointers on different 28 // states of the system. The pure header view is really the 29 // union of all these pointer which can be confusing. 30 // 31 // rc = ref counted 32 // w = weak pointer 33 // o = owning pointer 34 // 35 // 1) Situation after handle_wait_async(port, handle) is issued: 36 // 37 // 38 // +----------+ +--------+ 39 // | object | | Port | 40 // | | | | 41 // +----------+ +-----------+ +-+------+ 42 // | state | w | Port | ^ 43 // | tracker +------> | Observer | | 44 // +----------+ | | rc | 45 // | +---------+ 46 // +-----------+ 47 // | Port | 48 // | Packet | 49 // +-----------+ 50 // 51 // State changes of the object are propagated from the object 52 // to the port via |w| --> observer --> |rc| calls. 53 // 54 // 2) Situation after the packet is queued in the one-shot case on 55 // signal match or the wait is canceled. 56 // 57 // +----------+ +--------+ 58 // | object | | Port | 59 // | | | | 60 // +----------+ +-----------+ +-+---+--+ 61 // | state | | Port | ^ | 62 // | tracker | | Observer | | | 63 // +----------+ | | rc | | 64 // +---> | +---------+ | 65 // | +-----------+ | list 66 // o1 | | Port | o2 | 67 // +-----| Packet | <-----------+ 68 // +-----------+ 69 // 70 // Note that the object no longer has a |w| to the observer 71 // but the observer still owns the port via |rc|. 72 // 73 // For repeating ports |w| is always valid until the wait is 74 // canceled. 75 // 76 // The |o1| pointer is used to destroy the port observer only 77 // when cancellation happens and the port still owns the packet. 78 // 79 80 class ExceptionPort; 81 class PortDispatcher; 82 class PortObserver; 83 struct PortPacket; 84 85 struct PortAllocator { 86 virtual ~PortAllocator() = default; 87 88 virtual PortPacket* Alloc() = 0; 89 virtual void Free(PortPacket* port_packet) = 0; 90 }; 91 92 struct PortPacket final : public fbl::DoublyLinkedListable<PortPacket*> { 93 zx_port_packet_t packet; 94 const void* const handle; 95 ktl::unique_ptr<const PortObserver> observer; 96 PortAllocator* const allocator; 97 98 PortPacket(const void* handle, PortAllocator* allocator); 99 PortPacket(const PortPacket&) = delete; 100 void operator=(PortPacket) = delete; 101 keyfinal102 uint64_t key() const { return packet.key; } is_ephemeralfinal103 bool is_ephemeral() const { return allocator != nullptr; } Freefinal104 void Free() { allocator->Free(this); } 105 }; 106 107 struct PortInterruptPacket final : public fbl::DoublyLinkedListable<PortInterruptPacket*> { 108 zx_time_t timestamp; 109 uint64_t key; 110 }; 111 112 // Observers are weakly contained in state trackers until |remove_| member 113 // is false at the end of one of OnInitialize(), OnStateChange() or OnCancel() 114 // callbacks. 115 class PortObserver final : public StateObserver { 116 public: 117 PortObserver(uint32_t type, const Handle* handle, fbl::RefPtr<PortDispatcher> port, 118 uint64_t key, zx_signals_t signals); 119 ~PortObserver() = default; 120 121 private: 122 PortObserver(const PortObserver&) = delete; 123 PortObserver& operator=(const PortObserver&) = delete; 124 125 // StateObserver overrides. 126 Flags OnInitialize(zx_signals_t initial_state, const StateObserver::CountInfo* cinfo) final; 127 Flags OnStateChange(zx_signals_t new_state) final; 128 Flags OnCancel(const Handle* handle) final; 129 Flags OnCancelByKey(const Handle* handle, const void* port, uint64_t key) final; 130 void OnRemoved() final; 131 132 // The following method can only be called from 133 // OnInitialize(), OnStateChange() and OnCancel(). 134 Flags MaybeQueue(zx_signals_t new_state, uint64_t count); 135 136 const uint32_t type_; 137 const zx_signals_t trigger_; 138 PortPacket packet_; 139 140 fbl::RefPtr<PortDispatcher> const port_; 141 }; 142 143 // The PortDispatcher implements the port kernel object which is the cornerstone 144 // for waiting on object changes in Zircon. The PortDispatcher handles 4 usage 145 // cases: 146 // 1- Exception notification: task_bind_exception_port() 147 // 2- Object state change notification: zx_object_wait_async() 148 // a) single-shot mode 149 // b) repeating mode 150 // 3- Manual queuing: zx_port_queue() 151 // 4- Interrupt change notification: zx_interrupt_bind() 152 // 153 // This makes the implementation non-trivial. Cases 1, 2 and 3 uses the 154 // |packets_| linked list and case 4 uses |interrupt_packets_| linked list. 155 // 156 // The threads that wish to receive notifications block on Dequeue() (which 157 // maps to zx_port_wait()) and will receive packets from any of the four sources 158 // depending on what kind of object the port has been 'bound' to. 159 // 160 // When a packet from any of the sources arrives to the port, one waiting 161 // thread unblocks and gets the packet. In all cases |sema_| is used to signal 162 // and manage the waiting threads. 163 164 class PortDispatcher final : public SoloDispatcher<PortDispatcher, ZX_DEFAULT_PORT_RIGHTS> { 165 public: 166 static void Init(); 167 static PortAllocator* DefaultPortAllocator(); 168 static zx_status_t Create(uint32_t options, fbl::RefPtr<Dispatcher>* dispatcher, 169 zx_rights_t* rights); 170 171 ~PortDispatcher() final; get_type()172 zx_obj_type_t get_type() const final { return ZX_OBJ_TYPE_PORT; } 173 can_bind_to_interrupt()174 bool can_bind_to_interrupt() const { return options_ & ZX_PORT_BIND_TO_INTERRUPT; } 175 void on_zero_handles() final; 176 177 zx_status_t Queue(PortPacket* port_packet, zx_signals_t observed, uint64_t count); 178 zx_status_t QueueUser(const zx_port_packet_t& packet); 179 bool QueueInterruptPacket(PortInterruptPacket* port_packet, zx_time_t timestamp); 180 zx_status_t Dequeue(zx_time_t deadline, TimerSlack slack, zx_port_packet_t* packet); 181 bool RemoveInterruptPacket(PortInterruptPacket* port_packet); 182 183 // Decides who is going to destroy the observer. If it returns the 184 // observer back if it is the duty of the caller. It returns 185 // nullptr if it is the duty of the port. 186 ktl::unique_ptr<PortObserver> MaybeReap(ktl::unique_ptr<PortObserver> observer, 187 PortPacket* port_packet); 188 189 // Called under the handle table lock. 190 zx_status_t MakeObserver(uint32_t options, Handle* handle, uint64_t key, zx_signals_t signals); 191 192 // Returns true if at least one packet was removed from the queue. 193 // Called under the handle table lock when |handle| is not null. 194 // When |handle| is null, ephemeral PortPackets are removed from the queue but not freed. 195 bool CancelQueued(const void* handle, uint64_t key); 196 197 private: 198 friend class ExceptionPort; 199 200 explicit PortDispatcher(uint32_t options); 201 202 // Adopts a RefPtr to |eport|, and adds it to |eports_|. 203 // Called by ExceptionPort. 204 void LinkExceptionPort(ExceptionPort* eport); 205 206 // Removes |eport| from |eports_|, dropping its RefPtr. 207 // Does nothing if |eport| is not on the list. 208 // Called by ExceptionPort. 209 void UnlinkExceptionPort(ExceptionPort* eport); 210 211 fbl::Canary<fbl::magic("PORT")> canary_; 212 const uint32_t options_; 213 Semaphore sema_; 214 bool zero_handles_ TA_GUARDED(get_lock()); 215 216 // Next three members handle the object, manual and exception notifications. 217 size_t num_packets_ TA_GUARDED(get_lock()); 218 fbl::DoublyLinkedList<PortPacket*> packets_ TA_GUARDED(get_lock()); 219 fbl::DoublyLinkedList<fbl::RefPtr<ExceptionPort>> eports_ TA_GUARDED(get_lock()); 220 // Next two members handle the interrupt notifications. 221 DECLARE_SPINLOCK(PortDispatcher) spinlock_; 222 fbl::DoublyLinkedList<PortInterruptPacket*> interrupt_packets_ TA_GUARDED(spinlock_); 223 }; 224