1// Copyright 2016 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/handle.h>
8
9#include <object/dispatcher.h>
10#include <fbl/arena.h>
11#include <fbl/mutex.h>
12#include <lib/counters.h>
13#include <pow2.h>
14
15namespace {
16
17// The number of possible handles in the arena.
18constexpr size_t kMaxHandleCount = 256 * 1024u;
19
20// Warning level: high_handle_count() is called when
21// there are this many outstanding handles.
22constexpr size_t kHighHandleCount = (kMaxHandleCount * 7) / 8;
23
24KCOUNTER(handle_count_new, "kernel.handles.new");
25KCOUNTER(handle_count_duped, "kernel.handles.duped");
26KCOUNTER(handle_count_freed, "kernel.handles.freed");
27
28// Masks for building a Handle's base_value, which ProcessDispatcher
29// uses to create zx_handle_t values.
30//
31// base_value bit fields:
32//   [31..30]: Must be zero
33//   [29..kHandleGenerationShift]: Generation number
34//                                 Masked by kHandleGenerationMask
35//   [kHandleGenerationShift-1..0]: Index into handle_arena
36//                                  Masked by kHandleIndexMask
37constexpr uint32_t kHandleIndexMask = kMaxHandleCount - 1;
38static_assert((kHandleIndexMask & kMaxHandleCount) == 0,
39              "kMaxHandleCount must be a power of 2");
40constexpr uint32_t kHandleGenerationMask = ~kHandleIndexMask & ~(3 << 30);
41constexpr uint32_t kHandleGenerationShift = log2_uint_floor(kMaxHandleCount);
42static_assert(((3 << (kHandleGenerationShift - 1)) & kHandleGenerationMask) ==
43                  1 << kHandleGenerationShift,
44              "Shift is wrong");
45static_assert((kHandleGenerationMask >> kHandleGenerationShift) >= 255,
46              "Not enough room for a useful generation count");
47static_assert(((3 << 30) ^ kHandleGenerationMask ^ kHandleIndexMask) ==
48                  0xffffffffu,
49              "Masks do not agree");
50
51}  // namespace
52
53fbl::Arena Handle::arena_;
54
55void Handle::Init() TA_NO_THREAD_SAFETY_ANALYSIS {
56    arena_.Init("handles", sizeof(Handle), kMaxHandleCount);
57}
58
59// Returns a new |base_value| based on the value stored in the free
60// arena slot pointed to by |addr|. The new value will be different
61// from the last |base_value| used by this slot.
62uint32_t Handle::GetNewBaseValue(void* addr) TA_REQ(ArenaLock::Get()) {
63    // Get the index of this slot within the arena.
64    uint32_t handle_index = HandleToIndex(reinterpret_cast<Handle*>(addr));
65    DEBUG_ASSERT((handle_index & ~kHandleIndexMask) == 0);
66
67    // Check the free memory for a stashed base_value.
68    uint32_t v = *reinterpret_cast<uint32_t*>(addr);
69    uint32_t old_gen = 0;
70    if (v != 0) {
71        // This slot has been used before.
72        DEBUG_ASSERT((v & kHandleIndexMask) == handle_index);
73        old_gen = (v & kHandleGenerationMask) >> kHandleGenerationShift;
74    }
75    uint32_t new_gen =
76        (((old_gen + 1) << kHandleGenerationShift) & kHandleGenerationMask);
77    return (handle_index | new_gen);
78}
79
80// Allocate space for a Handle from the arena, but don't instantiate the
81// object.  |base_value| gets the value for Handle::base_value_.  |what|
82// says whether this is allocation or duplication, for the error message.
83void* Handle::Alloc(const fbl::RefPtr<Dispatcher>& dispatcher,
84                    const char* what, uint32_t* base_value) {
85    size_t outstanding_handles;
86    {
87        Guard<fbl::Mutex> guard{ArenaLock::Get()};
88        void* addr = arena_.Alloc();
89        outstanding_handles = arena_.DiagnosticCount();
90        if (likely(addr)) {
91            if (outstanding_handles > kHighHandleCount) {
92                // TODO: Avoid calling this for every handle after
93                // kHighHandleCount; printfs are slow and we're
94                // holding the mutex.
95                printf("WARNING: High handle count: %zu handles\n",
96                       outstanding_handles);
97            }
98            dispatcher->increment_handle_count();
99            *base_value = GetNewBaseValue(addr);
100            return addr;
101        }
102    }
103
104    printf("WARNING: Could not allocate %s handle (%zu outstanding)\n",
105           what, outstanding_handles);
106    return nullptr;
107}
108
109HandleOwner Handle::Make(fbl::RefPtr<Dispatcher> dispatcher,
110                         zx_rights_t rights) {
111    uint32_t base_value;
112    void* addr = Alloc(dispatcher, "new", &base_value);
113    if (unlikely(!addr))
114        return nullptr;
115    kcounter_add(handle_count_new, 1);
116    return HandleOwner(new (addr) Handle(fbl::move(dispatcher),
117                                         rights, base_value));
118}
119
120// Called only by Make.
121Handle::Handle(fbl::RefPtr<Dispatcher> dispatcher, zx_rights_t rights,
122               uint32_t base_value)
123    : process_id_(0u),
124      dispatcher_(fbl::move(dispatcher)),
125      rights_(rights),
126      base_value_(base_value) {
127}
128
129HandleOwner Handle::Dup(Handle* source, zx_rights_t rights) {
130    uint32_t base_value;
131    void* addr = Alloc(source->dispatcher(), "duplicate", &base_value);
132    if (unlikely(!addr))
133        return nullptr;
134    kcounter_add(handle_count_duped, 1);
135    return HandleOwner(new (addr) Handle(source, rights, base_value));
136}
137
138// Called only by Dup.
139Handle::Handle(Handle* rhs, zx_rights_t rights, uint32_t base_value)
140    : process_id_(rhs->process_id()),
141      dispatcher_(rhs->dispatcher_),
142      rights_(rights),
143      base_value_(base_value) {
144}
145
146// Destroys, but does not free, the Handle, and fixes up its memory to protect
147// against stale pointers to it. Also stashes the Handle's base_value for reuse
148// the next time this slot is allocated.
149void Handle::TearDown() TA_EXCL(ArenaLock::Get()) {
150    uint32_t old_base_value = base_value();
151
152    // Calling the handle dtor can cause many things to happen, so it is
153    // important to call it outside the lock.
154    this->~Handle();
155
156    // There may be stale pointers to this slot. Zero out most of its fields
157    // to ensure that the Handle does not appear to belong to any process
158    // or point to any Dispatcher.
159    memset(this, 0, sizeof(*this));
160
161    // Hold onto the base_value for the next user of this slot, stashing
162    // it at the beginning of the free slot.
163    *reinterpret_cast<uint32_t*>(this) = old_base_value;
164
165    // Double-check that the process_id field is zero, ensuring that
166    // no process can refer to this slot while it's free. This isn't
167    // completely legal since |handle| points to unconstructed memory,
168    // but it should be safe enough for an assertion.
169    DEBUG_ASSERT(process_id() == 0);
170}
171
172void Handle::Delete() {
173    fbl::RefPtr<Dispatcher> disp = dispatcher();
174
175    if (disp->has_state_tracker())
176        disp->Cancel(this);
177
178    TearDown();
179
180    bool zero_handles = false;
181    {
182        Guard<fbl::Mutex> guard{ArenaLock::Get()};
183        zero_handles = disp->decrement_handle_count();
184        arena_.Free(this);
185    }
186
187    if (zero_handles)
188        disp->on_zero_handles();
189
190    // If |disp| is the last reference then the dispatcher object
191    // gets destroyed here.
192    kcounter_add(handle_count_freed, 1);
193}
194
195Handle* Handle::FromU32(uint32_t value) TA_NO_THREAD_SAFETY_ANALYSIS {
196    uintptr_t handle_addr = IndexToHandle(value & kHandleIndexMask);
197    {
198        Guard<fbl::Mutex> guard{ArenaLock::Get()};
199        if (unlikely(!arena_.in_range(handle_addr)))
200            return nullptr;
201    }
202    auto handle = reinterpret_cast<Handle*>(handle_addr);
203    return likely(handle->base_value() == value) ? handle : nullptr;
204}
205
206uint32_t Handle::Count(const fbl::RefPtr<const Dispatcher>& dispatcher) {
207    // Handle::ArenaLock also guards Dispatcher::handle_count_.
208    Guard<fbl::Mutex> guard{ArenaLock::Get()};
209    return dispatcher->current_handle_count();
210}
211
212size_t Handle::diagnostics::OutstandingHandles() {
213    Guard<fbl::Mutex> guard{ArenaLock::Get()};
214    return arena_.DiagnosticCount();
215}
216
217void Handle::diagnostics::DumpTableInfo() {
218    Guard<fbl::Mutex> guard{ArenaLock::Get()};
219    arena_.Dump();
220}
221