Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 48 additions & 11 deletions kernel/drivers/graphics/gfxfb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -346,26 +346,63 @@ int32_t gfxfb_node::on_close(fs::file* f) {
client* closing = &client_of(f);

// Each accepted present's end queues an event in its client, which must outlive those ends and keeps control
sync::irq_state state = sync::spin_lock_irqsave(m_outputs_lock);
while (holds_unended_present(*closing)) {
state = sync::wait(closing->event_waiters, m_outputs_lock, state);
}

sync::spin_unlock_irqrestore(m_outputs_lock, state);
wait_for_unended_presents(*closing);

sync::mutex_lock(m_clients_lock);
if (m_controller == closing) {
bool is_controller = m_controller == closing;
sync::mutex_unlock(m_clients_lock);

// Control ends only after waiting for the base buffers' presents, so no other client's present comes before theirs
if (is_controller) {
give_outputs_back(*closing);

sync::mutex_lock(m_clients_lock);
m_controller = nullptr;
m_control_taken = false;
sync::mutex_unlock(m_clients_lock);
}

sync::mutex_unlock(m_clients_lock);

heap::kfree_delete(closing);

return fs::OK;
}

// Waits, holding no lock while asleep, until the display has ended each present of `owner` it accepted
void gfxfb_node::wait_for_unended_presents(client& owner) {
sync::irq_state state = sync::spin_lock_irqsave(m_outputs_lock);
while (holds_unended_present(owner)) {
state = sync::wait(owner.event_waiters, m_outputs_lock, state);
}

sync::spin_unlock_irqrestore(m_outputs_lock, state);
}

// Drops the events of `owner`, the controlling client, and presents each output's recorded base buffer where the output
// shows another buffer, naming no region. Waits for the display to end those it accepts
void gfxfb_node::give_outputs_back(client& owner) {
// No file reads `owner`'s events any more, and `queue_present()` refuses an output whose event `owner` holds unread
sync::irq_state state = sync::spin_lock_irqsave(owner.events_lock);
owner.event_count = 0;
sync::spin_unlock_irqrestore(owner.events_lock, state);

for (uint32_t output = 0; output < clamped_output_count(*m_display); output++) {
rc::strong_ref<buffer_record> base;

state = sync::spin_lock_irqsave(m_outputs_lock);
if (m_outputs[output].shown != m_outputs[output].base) {
base = m_outputs[output].base;
}

sync::spin_unlock_irqrestore(m_outputs_lock, state);

if (base) {
queue_present(owner, output, 0, base, nullptr, 0);
}
}

wait_for_unended_presents(owner);
}

ssize_t gfxfb_node::read(fs::file* f, void* buf, size_t count, uint32_t flags) {
if (count < sizeof(gfxfb_event)) {
return fs::ERR_INVAL;
Expand Down Expand Up @@ -784,8 +821,8 @@ __PRIVILEGED_CODE void gfxfb_node::present_done(uint32_t output, const present_r
break;
}

// The wake stays under the lock, since a closing presenter is freed once it takes the lock and finds no unended
// present of its own
// The wake stays under the lock, since a closing presenter may be freed as soon as it takes the lock and finds no
// unended present of its own
client* presenter = records.presenter;
records.presenter = nullptr;
queue_event(*presenter, present_event(output, records.presented_handle, result));
Expand Down
9 changes: 7 additions & 2 deletions kernel/drivers/graphics/gfxfb_abi.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,13 @@
* its size, returning the bytes read by then, or the failing read's error when
* there are none. `poll` reports the file readable while it holds an event,
* and never writable. A file that closes while a present of it has not ended
* waits for that end, whose event then reaches no file, and keeps control
* through the wait.
* waits for that end, whose event then reaches no file. A closing file that
* controls the buffers then drops the events it holds and presents the base
* buffer of each output showing another buffer, as a present of handle 0
* naming no region. It also waits for the ends of those the device accepts,
* and their events reach no file either. It keeps control through both waits.
* An output where the base buffer's present does not latch, as on a dark
* output or a failed device, keeps showing the buffer it showed.
*
* The kernel and userland are built from one tree, so a structure may gain
* fields when its call gains a feature. Each structure holds fixed-width
Expand Down
17 changes: 12 additions & 5 deletions kernel/drivers/graphics/gfxfb_node.h
Original file line number Diff line number Diff line change
Expand Up @@ -170,11 +170,16 @@ class boot_display : public display {
* `fs::ERR_AGAIN` when a non-blocking read finds none, and with `fs::ERR_INTR`
* when a signal ends its wait for one. `poll` reports a client holding events
* readable. A client that closes while a present of it has not ended waits for
* that end, holding no lock, before it ends its control and is freed. Like
* every `fs::node`, it lives on the privileged heap, so its callers must run
* elevated. devfs keeps a published node for good, and a node is never freed
* once built, since the reaper may still hand a base buffer back to its boot
* display.
* that end, holding no lock. A closing client that controls the buffers then
* drops its events and presents each output's recorded base buffer where the
* output shows another buffer, naming no region. It ends its control only once
* the display has ended those presents it accepted. An output with no base
* buffer recorded, or whose base buffer's present is refused or ends dark or
* failed, keeps showing the buffer it showed. A closing client is then freed
* with the events it holds. Like every `fs::node`, a gfxfb node lives on the
* privileged heap, so its callers must run elevated. devfs keeps a published
* node for good, and a node is never freed once built, since the reaper may
* still hand a base buffer back to its boot display.
*/
class gfxfb_node : public fs::node {
public:
Expand Down Expand Up @@ -256,6 +261,8 @@ class gfxfb_node : public fs::node {
uint32_t region_count
);
bool holds_unended_present(const client& owner) const;
void wait_for_unended_presents(client& owner);
void give_outputs_back(client& owner);
bool begin_controlled_call(client& caller);
void end_controlled_call(bool succeeded);
uint32_t take_handle(client& owner);
Expand Down
Loading
Loading