This repository was archived by the owner on May 26, 2026. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpager_dispatcher.cpp
More file actions
227 lines (191 loc) · 7.22 KB
/
Copy pathpager_dispatcher.cpp
File metadata and controls
227 lines (191 loc) · 7.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
// Copyright 2018 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <object/pager_dispatcher.h>
#include <object/thread_dispatcher.h>
#include <trace.h>
#include <vm/page_source.h>
#define LOCAL_TRACE 0
zx_status_t PagerDispatcher::Create(fbl::RefPtr<Dispatcher>* dispatcher, zx_rights_t* rights) {
fbl::AllocChecker ac;
auto disp = new (&ac) PagerDispatcher();
if (!ac.check()) {
return ZX_ERR_NO_MEMORY;
}
*rights = default_rights();
*dispatcher = fbl::AdoptRef<Dispatcher>(disp);
return ZX_OK;
}
PagerDispatcher::PagerDispatcher() : SoloDispatcher() {}
PagerDispatcher::~PagerDispatcher() {
DEBUG_ASSERT(srcs_.is_empty());
}
zx_status_t PagerDispatcher::CreateSource(fbl::RefPtr<PortDispatcher> port,
uint64_t key, fbl::RefPtr<PageSource>* src_out) {
fbl::AllocChecker ac;
auto src = fbl::AdoptRef(new (&ac) PagerSource(this, ktl::move(port), key));
if (!ac.check()) {
return ZX_ERR_NO_MEMORY;
}
Guard<fbl::Mutex> guard{&list_mtx_};
srcs_.push_front(src);
*src_out = ktl::move(src);
return ZX_OK;
}
fbl::RefPtr<PagerSource> PagerDispatcher::ReleaseSource(PagerSource* src) {
Guard<fbl::Mutex> guard{&list_mtx_};
return src->InContainer() ? srcs_.erase(*src) : nullptr;
}
void PagerDispatcher::on_zero_handles() {
Guard<fbl::Mutex> guard{&list_mtx_};
while (!srcs_.is_empty()) {
fbl::RefPtr<PagerSource> src = srcs_.pop_front();
// Call unlocked to prevent a double-lock if PagerDispatcher::ReleaseSource is called,
// and to preserve the lock order that PagerSource locks are acquired before the
// list lock.
guard.CallUnlocked([&src]() mutable {
src->Close();
src->OnDispatcherClosed();
});
}
}
PagerSource::PagerSource(PagerDispatcher* dispatcher,
fbl::RefPtr<PortDispatcher> port, uint64_t key)
: PageSource(), pager_(dispatcher), port_(ktl::move(port)), key_(key) {
LTRACEF("%p key %lx\n", this, key_);
}
PagerSource::~PagerSource() {
LTRACEF("%p\n", this);
DEBUG_ASSERT(closed_);
DEBUG_ASSERT(!complete_pending_);
}
void PagerSource::GetPageAsync(page_request_t* request) {
Guard<fbl::Mutex> guard{&mtx_};
ASSERT(!closed_);
QueueMessageLocked(request);
}
void PagerSource::QueueMessageLocked(page_request_t* request) {
if (packet_busy_) {
list_add_tail(&pending_requests_, &request->node);
return;
}
packet_busy_ = true;
active_request_ = request;
uint64_t offset, length;
uint16_t cmd;
if (request != &complete_request_) {
cmd = ZX_PAGER_VMO_READ;
offset = request->offset;
length = request->length;
// The vm subsystem should guarantee this
uint64_t unused;
DEBUG_ASSERT(!add_overflow(offset, length, &unused));
} else {
offset = length = 0;
cmd = ZX_PAGER_VMO_COMPLETE;
}
zx_port_packet_t packet = {};
packet.key = key_;
packet.type = ZX_PKT_TYPE_PAGE_REQUEST;
packet.page_request.command = cmd;
packet.page_request.offset = offset;
packet.page_request.length = length;
packet_.packet = packet;
// We can treat ZX_ERR_BAD_STATE as if the packet was queued
// but the pager service never responds.
// TODO: Bypass the port's max queued packet count to prevent ZX_ERR_SHOULD_WAIT
ASSERT(port_->Queue(&packet_, ZX_SIGNAL_NONE, 0) != ZX_ERR_SHOULD_WAIT);
}
void PagerSource::ClearAsyncRequest(page_request_t* request) {
Guard<fbl::Mutex> guard{&mtx_};
ASSERT(!closed_);
if (request == active_request_) {
// Condition on whether or not we atually cancel the packet, to make sure
// we don't race with a call to PagerSource::Free.
if (port_->CancelQueued(&packet_)) {
OnPacketFreedLocked();
}
} else if (list_in_list(&request->node)) {
list_delete(&request->node);
}
}
void PagerSource::SwapRequest(page_request_t* old, page_request_t* new_req) {
Guard<fbl::Mutex> guard{&mtx_};
ASSERT(!closed_);
if (list_in_list(&old->node)) {
list_replace_node(&old->node, &new_req->node);
} else if (old == active_request_) {
active_request_ = new_req;
}
}
void PagerSource::OnDetach() {
Guard<fbl::Mutex> guard{&mtx_};
ASSERT(!closed_);
complete_pending_ = true;
QueueMessageLocked(&complete_request_);
}
void PagerSource::OnClose() {
fbl::RefPtr<PagerSource> self;
Guard<fbl::Mutex> guard{&mtx_};
ASSERT(!closed_);
closed_ = true;
if (!complete_pending_) {
// We know PagerDispatcher::on_zero_handles hasn't been invoked, since that would
// have already closed this pager source.
self = pager_->ReleaseSource(this);
} // else this is released in PagerSource::Free
}
void PagerSource::OnDispatcherClosed() {
// The pager dispatcher's reference to this object is the only one we completely control. Now
// that it's gone, we need to make sure that port_ doesn't end up with an invalid pointer
// to packet_ if all external RefPtrs to this object go away.
Guard<fbl::Mutex> guard{&mtx_};
if (complete_pending_) {
if (port_->CancelQueued(&packet_)) {
// We successfully cancelled the message, so we don't have to worry about
// PagerSource::Free being called.
complete_pending_ = false;
} else {
// If we failed to cancel the message, then there is a pending call to
// PagerSource::Free. We need to make sure the object isn't deleted too early,
// so have it keep a reference to itself, which PagerSource::Free will then
// clean up.
self_ref_ = fbl::WrapRefPtr(this);
}
} else {
// Either the complete message had already been dispatched when this object was closed or
// PagerSource::Free was called between this object being closed and this method taking the
// lock. In either case, the port no longer has a reference and cleanup is already done.
}
}
void PagerSource::Free(PortPacket* packet) {
fbl::RefPtr<PagerSource> self;
Guard<fbl::Mutex> guard{&mtx_};
if (active_request_ != &complete_request_) {
OnPacketFreedLocked();
} else {
complete_pending_ = false;
if (closed_) {
// If the source is closed, we need to do delayed cleanup. If the dispatcher
// has already been torn down, then there's a self-reference we need to clean
// up. Otherwise, clean up the dispatcher's reference to us.
self = std::move(self_ref_);
if (!self) {
self = pager_->ReleaseSource(this);
}
}
}
}
void PagerSource::OnPacketFreedLocked() {
packet_busy_ = false;
active_request_ = nullptr;
if (!list_is_empty(&pending_requests_)) {
QueueMessageLocked(list_remove_head_type(&pending_requests_, page_request, node));
}
}
zx_status_t PagerSource::WaitOnEvent(event_t* event) {
ThreadDispatcher::AutoBlocked by(ThreadDispatcher::Blocked::PAGER);
return event_wait_deadline(event, ZX_TIME_INFINITE, true);
}