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