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 #include <object/port_dispatcher.h>
8 
9 #include <assert.h>
10 #include <err.h>
11 #include <platform.h>
12 #include <pow2.h>
13 
14 #include <fbl/alloc_checker.h>
15 #include <fbl/arena.h>
16 #include <fbl/auto_lock.h>
17 #include <lib/counters.h>
18 #include <object/excp_port.h>
19 #include <object/handle.h>
20 #include <object/process_dispatcher.h>
21 #include <object/thread_dispatcher.h>
22 #include <zircon/compiler.h>
23 #include <zircon/rights.h>
24 #include <zircon/syscalls/port.h>
25 #include <zircon/types.h>
26 
27 // All port sub-packets must be exactly 32 bytes
28 static_assert(sizeof(zx_packet_user_t) == 32, "incorrect size for zx_packet_signal_t");
29 static_assert(sizeof(zx_packet_signal_t) == 32, "incorrect size for zx_packet_signal_t");
30 static_assert(sizeof(zx_packet_exception_t) == 32, "incorrect size for zx_packet_exception_t");
31 static_assert(sizeof(zx_packet_guest_bell_t) == 32, "incorrect size for zx_packet_guest_bell_t");
32 static_assert(sizeof(zx_packet_guest_mem_t) == 32, "incorrect size for zx_packet_guest_mem_t");
33 static_assert(sizeof(zx_packet_guest_io_t) == 32, "incorrect size for zx_packet_guest_io_t");
34 static_assert(sizeof(zx_packet_guest_vcpu_t) == 32, "incorrect size for zx_packet_guest_vcpu_t");
35 static_assert(sizeof(zx_packet_interrupt_t) == 32, "incorrect size for zx_packet_interrupt_t");
36 
37 KCOUNTER(port_arena_count, "kernel.port.arena.count");
38 KCOUNTER(port_full_count, "kernel.port.full.count");
39 
40 class ArenaPortAllocator final : public PortAllocator {
41 public:
42     zx_status_t Init();
43     virtual ~ArenaPortAllocator() = default;
44 
45     virtual PortPacket* Alloc();
46     virtual void Free(PortPacket* port_packet);
47 
48 private:
49     fbl::TypedArena<PortPacket, fbl::Mutex> arena_;
50 };
51 
52 namespace {
53 constexpr size_t kMaxPendingPacketCount = 16 * 1024u;
54 
55 // TODO(maniscalco): Enforce this limit per process via the job policy.
56 constexpr size_t kMaxPendingPacketCountPerPort = kMaxPendingPacketCount / 8;
57 ArenaPortAllocator port_allocator;
58 } // namespace.
59 
Init()60 zx_status_t ArenaPortAllocator::Init() {
61     return arena_.Init("packets", kMaxPendingPacketCount);
62 }
63 
Alloc()64 PortPacket* ArenaPortAllocator::Alloc() {
65     PortPacket* packet = arena_.New(nullptr, this);
66     if (packet == nullptr) {
67         printf("WARNING: Could not allocate new port packet\n");
68         return nullptr;
69     }
70     kcounter_add(port_arena_count, 1);
71     return packet;
72 }
73 
Free(PortPacket * port_packet)74 void ArenaPortAllocator::Free(PortPacket* port_packet) {
75     arena_.Delete(port_packet);
76     kcounter_add(port_arena_count, -1);
77 }
78 
PortPacket(const void * handle,PortAllocator * allocator)79 PortPacket::PortPacket(const void* handle, PortAllocator* allocator)
80     : packet{}, handle(handle), observer(nullptr), allocator(allocator) {
81     // Note that packet is initialized to zeros.
82     if (handle) {
83         // Currently |handle| is only valid if the packets are not ephemeral
84         // which means that PortObserver always uses the kernel heap.
85         DEBUG_ASSERT(allocator == nullptr);
86     }
87 }
88 
PortObserver(uint32_t type,const Handle * handle,fbl::RefPtr<PortDispatcher> port,uint64_t key,zx_signals_t signals)89 PortObserver::PortObserver(uint32_t type, const Handle* handle, fbl::RefPtr<PortDispatcher> port,
90                            uint64_t key, zx_signals_t signals)
91     : type_(type),
92       trigger_(signals),
93       packet_(handle, nullptr),
94       port_(ktl::move(port)) {
95 
96     DEBUG_ASSERT(handle != nullptr);
97 
98     auto& packet = packet_.packet;
99     packet.status = ZX_OK;
100     packet.key = key;
101     packet.type = type_;
102     packet.signal.trigger = trigger_;
103 }
104 
OnInitialize(zx_signals_t initial_state,const StateObserver::CountInfo * cinfo)105 StateObserver::Flags PortObserver::OnInitialize(zx_signals_t initial_state,
106                                                 const StateObserver::CountInfo* cinfo) {
107     uint64_t count = 1u;
108 
109     if (cinfo) {
110         for (const auto& entry : cinfo->entry) {
111             if ((entry.signal & trigger_) && (entry.count > 0u)) {
112                 count = entry.count;
113                 break;
114             }
115         }
116     }
117     return MaybeQueue(initial_state, count);
118 }
119 
OnStateChange(zx_signals_t new_state)120 StateObserver::Flags PortObserver::OnStateChange(zx_signals_t new_state) {
121     return MaybeQueue(new_state, 1u);
122 }
123 
OnCancel(const Handle * handle)124 StateObserver::Flags PortObserver::OnCancel(const Handle* handle) {
125     if (packet_.handle == handle) {
126         return kHandled | kNeedRemoval;
127     } else {
128         return 0;
129     }
130 }
131 
OnCancelByKey(const Handle * handle,const void * port,uint64_t key)132 StateObserver::Flags PortObserver::OnCancelByKey(const Handle* handle, const void* port, uint64_t key) {
133     if ((packet_.handle != handle) || (packet_.key() != key) || (port_.get() != port))
134         return 0;
135     return kHandled | kNeedRemoval;
136 }
137 
OnRemoved()138 void PortObserver::OnRemoved() {
139     // If observer ends up being non-null, it is ourself, and thus our
140     // responsibility to delete ourself.
141     ktl::unique_ptr<PortObserver> observer =
142         port_->MaybeReap(ktl::unique_ptr<PortObserver>(this), &packet_);
143 }
144 
MaybeQueue(zx_signals_t new_state,uint64_t count)145 StateObserver::Flags PortObserver::MaybeQueue(zx_signals_t new_state, uint64_t count) {
146     // Always called with the object state lock being held.
147     if ((trigger_ & new_state) == 0u)
148         return 0;
149 
150     // TODO(cpu): Queue() can fail and we don't propagate this information
151     // here properly. Now, this failure is self inflicted because we constrain
152     // the packet arena size artificially.  See ZX-2166 for details.
153     auto status = port_->Queue(&packet_, new_state, count);
154 
155     if ((type_ == ZX_PKT_TYPE_SIGNAL_ONE) || (status != ZX_OK))
156         return kNeedRemoval;
157 
158     return 0;
159 }
160 
161 /////////////////////////////////////////////////////////////////////////////////////////
162 
Init()163 void PortDispatcher::Init() {
164     port_allocator.Init();
165 }
166 
DefaultPortAllocator()167 PortAllocator* PortDispatcher::DefaultPortAllocator() {
168     return &port_allocator;
169 }
170 
Create(uint32_t options,fbl::RefPtr<Dispatcher> * dispatcher,zx_rights_t * rights)171 zx_status_t PortDispatcher::Create(uint32_t options, fbl::RefPtr<Dispatcher>* dispatcher,
172                                    zx_rights_t* rights) {
173     if (options && options != ZX_PORT_BIND_TO_INTERRUPT) {
174         return ZX_ERR_INVALID_ARGS;
175     }
176     fbl::AllocChecker ac;
177     auto disp = new (&ac) PortDispatcher(options);
178     if (!ac.check())
179         return ZX_ERR_NO_MEMORY;
180 
181     *rights = default_rights();
182     *dispatcher = fbl::AdoptRef<Dispatcher>(disp);
183     return ZX_OK;
184 }
185 
PortDispatcher(uint32_t options)186 PortDispatcher::PortDispatcher(uint32_t options)
187     : options_(options), zero_handles_(false), num_packets_(0u) {
188 }
189 
~PortDispatcher()190 PortDispatcher::~PortDispatcher() {
191     DEBUG_ASSERT(zero_handles_);
192     DEBUG_ASSERT(num_packets_ == 0u);
193 }
194 
on_zero_handles()195 void PortDispatcher::on_zero_handles() {
196     canary_.Assert();
197 
198     Guard<fbl::Mutex> guard{get_lock()};
199     zero_handles_ = true;
200 
201     // Unlink and unbind exception ports.
202     while (!eports_.is_empty()) {
203         auto eport = eports_.pop_back();
204 
205         // Tell the eport to unbind itself, then drop our ref to it. Called
206         // unlocked because the eport may call our ::UnlinkExceptionPort.
207         guard.CallUnlocked([&eport]() { eport->OnPortZeroHandles(); });
208     }
209 
210     // Free any queued packets.
211     while (!packets_.is_empty()) {
212         auto packet = packets_.pop_front();
213         --num_packets_;
214 
215         // If the packet is ephemeral, free it outside of the lock. Otherwise,
216         // reset the observer if it is present.
217         if (packet->is_ephemeral()) {
218             guard.CallUnlocked([packet]() {
219                     packet->Free();
220             });
221         } else {
222             // The reference to the port that the observer holds cannot be the last one
223             // because another reference was used to call on_zero_handles, so we don't
224             // need to worry about destroying ourselves.
225             packet->observer.reset();
226         }
227     }
228 }
229 
QueueUser(const zx_port_packet_t & packet)230 zx_status_t PortDispatcher::QueueUser(const zx_port_packet_t& packet) {
231     canary_.Assert();
232 
233     auto port_packet = port_allocator.Alloc();
234     if (!port_packet)
235         return ZX_ERR_NO_MEMORY;
236 
237     port_packet->packet = packet;
238     port_packet->packet.type = ZX_PKT_TYPE_USER;
239 
240     auto status = Queue(port_packet, 0u, 0u);
241     if (status != ZX_OK)
242         port_packet->Free();
243     return status;
244 }
245 
RemoveInterruptPacket(PortInterruptPacket * port_packet)246 bool PortDispatcher::RemoveInterruptPacket(PortInterruptPacket* port_packet) {
247     Guard<SpinLock, IrqSave> guard{&spinlock_};
248     if (port_packet->InContainer()) {
249         interrupt_packets_.erase(*port_packet);
250         return true;
251     }
252     return false;
253 }
254 
QueueInterruptPacket(PortInterruptPacket * port_packet,zx_time_t timestamp)255 bool PortDispatcher::QueueInterruptPacket(PortInterruptPacket* port_packet, zx_time_t timestamp) {
256     Guard<SpinLock, IrqSave> guard{&spinlock_};
257     if (port_packet->InContainer()) {
258         return false;
259     } else {
260         port_packet->timestamp = timestamp;
261         interrupt_packets_.push_back(port_packet);
262         sema_.Post();
263         return true;
264     }
265 }
266 
Queue(PortPacket * port_packet,zx_signals_t observed,uint64_t count)267 zx_status_t PortDispatcher::Queue(PortPacket* port_packet, zx_signals_t observed, uint64_t count) {
268     canary_.Assert();
269 
270     AutoReschedDisable resched_disable; // Must come before the lock guard.
271     Guard<fbl::Mutex> guard{get_lock()};
272     if (zero_handles_)
273         return ZX_ERR_BAD_STATE;
274 
275     if (num_packets_ > kMaxPendingPacketCountPerPort) {
276         kcounter_add(port_full_count, 1);
277         return ZX_ERR_SHOULD_WAIT;
278     }
279 
280     if (observed) {
281         if (port_packet->InContainer()) {
282             port_packet->packet.signal.observed |= observed;
283             // |count| is deliberately left as is.
284             return ZX_OK;
285         }
286         port_packet->packet.signal.observed = observed;
287         port_packet->packet.signal.count = count;
288     }
289     packets_.push_back(port_packet);
290     ++num_packets_;
291     // This Disable() call must come before Post() to be useful, but doing
292     // it earlier would also be OK.
293     resched_disable.Disable();
294     sema_.Post();
295 
296     return ZX_OK;
297 }
298 
Dequeue(zx_time_t deadline,TimerSlack slack,zx_port_packet_t * out_packet)299 zx_status_t PortDispatcher::Dequeue(zx_time_t deadline, TimerSlack slack,
300                                     zx_port_packet_t* out_packet) {
301     canary_.Assert();
302 
303     while (true) {
304         if (options_ == ZX_PORT_BIND_TO_INTERRUPT) {
305             Guard<SpinLock, IrqSave> guard{&spinlock_};
306             PortInterruptPacket* port_interrupt_packet = interrupt_packets_.pop_front();
307             if (port_interrupt_packet != nullptr) {
308                 *out_packet = {};
309                 out_packet->key = port_interrupt_packet->key;
310                 out_packet->type = ZX_PKT_TYPE_INTERRUPT;
311                 out_packet->status = ZX_OK;
312                 out_packet->interrupt.timestamp = port_interrupt_packet->timestamp;
313                 return ZX_OK;
314             }
315         }
316         {
317             Guard<fbl::Mutex> guard{get_lock()};
318             PortPacket* port_packet = packets_.pop_front();
319             if (port_packet != nullptr) {
320                 --num_packets_;
321                 *out_packet = port_packet->packet;
322 
323                 bool is_ephemeral = port_packet->is_ephemeral();
324                 // The reference to the port that the observer holds cannot be the last one
325                 // because another reference was used to call Dequeue, so we don't need to
326                 // worry about destroying ourselves.
327                 port_packet->observer.reset();
328                 guard.Release();
329 
330                 // If the packet is ephemeral, free it outside of the lock. We need to read
331                 // is_ephemeral inside the lock because it's possible for a non-ephemeral packet
332                 // to get deleted after a call to |MaybeReap| as soon as we release the lock.
333                 if (is_ephemeral) {
334                     port_packet->Free();
335                 }
336                 return ZX_OK;
337             }
338         }
339 
340         {
341             ThreadDispatcher::AutoBlocked by(ThreadDispatcher::Blocked::PORT);
342             zx_status_t st = sema_.Wait(deadline, slack);
343             if (st != ZX_OK)
344                 return st;
345         }
346     }
347 }
348 
MaybeReap(ktl::unique_ptr<PortObserver> observer,PortPacket * port_packet)349 ktl::unique_ptr<PortObserver> PortDispatcher::MaybeReap(ktl::unique_ptr<PortObserver> observer,
350                                                         PortPacket* port_packet) {
351     canary_.Assert();
352     DEBUG_ASSERT(!port_packet->is_ephemeral());
353 
354     Guard<fbl::Mutex> guard{get_lock()};
355     if (port_packet->InContainer()) {
356         // The destruction will happen when the packet is dequeued or in CancelQueued()
357         DEBUG_ASSERT(port_packet->observer == nullptr);
358         port_packet->observer = ktl::move(observer);
359     }
360     return observer;
361 }
362 
MakeObserver(uint32_t options,Handle * handle,uint64_t key,zx_signals_t signals)363 zx_status_t PortDispatcher::MakeObserver(uint32_t options, Handle* handle, uint64_t key,
364                                          zx_signals_t signals) {
365     canary_.Assert();
366 
367     // Called under the handle table lock.
368 
369     auto dispatcher = handle->dispatcher();
370     if (!dispatcher->is_waitable())
371         return ZX_ERR_NOT_SUPPORTED;
372 
373     uint32_t type;
374     switch (options) {
375         case ZX_WAIT_ASYNC_ONCE:
376             type = ZX_PKT_TYPE_SIGNAL_ONE;
377             break;
378         case ZX_WAIT_ASYNC_REPEATING:
379             type = ZX_PKT_TYPE_SIGNAL_REP;
380             break;
381         default:
382             return ZX_ERR_INVALID_ARGS;
383     }
384 
385     fbl::AllocChecker ac;
386     auto observer = new (&ac) PortObserver(type, handle, fbl::RefPtr<PortDispatcher>(this), key,
387                                            signals);
388     if (!ac.check())
389         return ZX_ERR_NO_MEMORY;
390 
391     dispatcher->add_observer(observer);
392     return ZX_OK;
393 }
394 
CancelQueued(const void * handle,uint64_t key)395 bool PortDispatcher::CancelQueued(const void* handle, uint64_t key) {
396     canary_.Assert();
397 
398     Guard<fbl::Mutex> guard{get_lock()};
399 
400     // This loop can take a while if there are many items.
401     // In practice, the number of pending signal packets is
402     // approximately the number of signaled _and_ watched
403     // objects plus the number of pending user-queued
404     // packets.
405     //
406     // There are two strategies to deal with too much
407     // looping here if that is seen in practice.
408     //
409     // 1. Swap the |packets_| list for an empty list and
410     //    release the lock. New arriving packets are
411     //    added to the empty list while the loop happens.
412     //    Readers will be blocked but the watched objects
413     //    will be fully operational. Once processing
414     //    is done the lists are appended.
415     //
416     // 2. Segregate user packets from signal packets
417     //    and deliver them in order via timestamps or
418     //    a side structure.
419 
420     bool packet_removed = false;
421 
422     for (auto it = packets_.begin(); it != packets_.end();) {
423         if ((it->handle == handle) && (it->key() == key)) {
424             auto to_remove = it++;
425             // Destroyed as we go around the loop.
426             ktl::unique_ptr<const PortObserver> observer =
427                 ktl::move(packets_.erase(to_remove)->observer);
428             --num_packets_;
429             packet_removed = true;
430         } else {
431             ++it;
432         }
433     }
434 
435     return packet_removed;
436 }
437 
LinkExceptionPort(ExceptionPort * eport)438 void PortDispatcher::LinkExceptionPort(ExceptionPort* eport) {
439     canary_.Assert();
440 
441     Guard<fbl::Mutex> guard{get_lock()};
442     DEBUG_ASSERT_COND(eport->PortMatches(this, /* allow_null */ false));
443     DEBUG_ASSERT(!eport->InContainer());
444     eports_.push_back(ktl::move(AdoptRef(eport)));
445 }
446 
UnlinkExceptionPort(ExceptionPort * eport)447 void PortDispatcher::UnlinkExceptionPort(ExceptionPort* eport) {
448     canary_.Assert();
449 
450     Guard<fbl::Mutex> guard{get_lock()};
451     DEBUG_ASSERT_COND(eport->PortMatches(this, /* allow_null */ true));
452     if (eport->InContainer()) {
453         eports_.erase(*eport);
454     }
455 }
456