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