gpu: use Buffer_id to identify buffers

Rather than using the dataspace capability directly, let the client
choose its own local identifier that is linked to the underlying
capability.

Fixes #4265.
This commit is contained in:
Josef Söntgen 2021-09-23 17:37:51 +02:00 committed by Norman Feske
parent 9a80c3a618
commit 3b40790e02
4 changed files with 310 additions and 307 deletions

View File

@ -152,6 +152,70 @@ static void dump_ioctl(unsigned long request)
}
namespace {
using Offset = unsigned long;
struct Gpu_virtual_address {
uint64_t addr;
};
} /* anonymous namespace */
struct Gpu::Buffer
{
Gpu::Connection &_gpu;
Genode::Id_space<Gpu::Buffer>::Element const _elem;
Genode::Dataspace_capability const cap;
Genode::size_t const size;
Constructible<Attached_dataspace> buffer_attached { };
Genode::Dataspace_capability map_cap { };
Offset map_offset { 0 };
Gpu_virtual_address gpu_vaddr { };
Gpu::Info::Execution_buffer_sequence seqno { };
bool gpu_vaddr_valid { false };
bool busy { false };
Buffer(Gpu::Connection &gpu,
Genode::size_t size,
Genode::Id_space<Buffer> &space)
:
_gpu { gpu },
_elem { *this, space },
cap { _gpu.alloc_buffer(_elem.id(), size) },
size { size }
{ }
virtual ~Buffer()
{
_gpu.free_buffer(_elem.id());
}
bool mmap(Genode::Env &env)
{
if (!buffer_attached.constructed())
buffer_attached.construct(env.rm(), cap);
return buffer_attached.constructed();
}
addr_t mmap_addr() {
return reinterpret_cast<addr_t>(buffer_attached->local_addr<addr_t>());
}
Gpu::Buffer_id id() const
{
return _elem.id();
}
};
class Drm_call
{
private:
@ -163,69 +227,9 @@ class Drm_call
size_t _available_gtt_size { _gpu_info.aperture_size };
bool _complete { false };
using Offset = unsigned long;
using Buffer = Gpu::Buffer;
struct Gpu_virtual_address {
uint64_t addr;
};
struct Buffer_handle;
typedef Genode::Id_space<Buffer_handle>::Element Handle;
typedef Genode::Id_space<Buffer_handle>::Id Handle_id;
struct Buffer_handle
{
Genode::Dataspace_capability const cap;
Genode::size_t const size;
Handle const handle;
Constructible<Attached_dataspace> buffer_attached { };
Genode::Dataspace_capability map_cap { };
Offset map_offset { 0 };
Gpu_virtual_address gpu_vaddr { };
Gpu::Info::Execution_buffer_sequence seqno { };
bool gpu_vaddr_valid { false };
bool busy { false };
Buffer_handle(Genode::Dataspace_capability cap,
Genode::size_t size,
Genode::Id_space<Buffer_handle> &space)
:
cap(cap), size(size),
handle(*this, space)
{
if (!cap.valid() || !size)
Genode::warning("invalid Buffer_handle ?");
}
virtual ~Buffer_handle() { }
bool valid() const { return cap.valid() && size != 0; }
bool mmap(Genode::Env &env)
{
if (!valid())
return false;
if (!buffer_attached.constructed())
buffer_attached.construct(env.rm(), cap);
return buffer_attached.constructed();
}
addr_t mmap_addr() {
return reinterpret_cast<addr_t>(buffer_attached->local_addr<addr_t>());
}
};
Genode::Id_space<Buffer_handle> _buffer_handles { };
using Buffer = Genode::Registered<Buffer_handle>;
Genode::Registry<Buffer> _buffer_registry { };
Genode::Id_space<Gpu::Buffer> _buffer_space { };
struct Sync_obj
{
@ -241,7 +245,7 @@ class Drm_call
Genode::Id_space<Sync_obj> _sync_objects { };
bool _map_buffer_ppgtt(Buffer_handle &buffer, Gpu_virtual_address const vaddr)
bool _map_buffer_ppgtt(Buffer &buffer, Gpu_virtual_address const vaddr)
{
if (buffer.gpu_vaddr_valid)
Genode::warning(__func__, " already have a gpu virtual address ",
@ -250,7 +254,7 @@ class Drm_call
/* XXX out of cap XXX */
bool const ppgtt = Genode::retry<Gpu::Session::Out_of_ram>(
[&]() { return _gpu_session.map_buffer_ppgtt(buffer.cap,
[&]() { return _gpu_session.map_buffer_ppgtt(buffer.id(),
Utils::limit_to_48bit(vaddr.addr)); },
[&]() { _gpu_session.upgrade_ram(4096); }
);
@ -265,11 +269,12 @@ class Drm_call
return true;
}
void _unmap_buffer_ppgtt(Buffer_handle &buffer)
void _unmap_buffer_ppgtt(Buffer &buffer)
{
if (!buffer.gpu_vaddr_valid) return;
_gpu_session.unmap_buffer_ppgtt(buffer.cap, Utils::limit_to_48bit(buffer.gpu_vaddr.addr));
_gpu_session.unmap_buffer_ppgtt(buffer.id(),
Utils::limit_to_48bit(buffer.gpu_vaddr.addr));
buffer.gpu_vaddr_valid = false;
}
@ -277,115 +282,94 @@ class Drm_call
void _alloc_buffer(uint64_t const size, FUNC const &fn)
{
Genode::size_t donate = size;
Genode::Dataspace_capability cap = Genode::retry<Gpu::Session::Out_of_ram>(
[&] () { return _gpu_session.alloc_buffer(size); },
Buffer *buffer = nullptr;
Genode::retry<Gpu::Session::Out_of_ram>(
[&] () {
buffer =
new (&_heap) Buffer(_gpu_session, size, _buffer_space);
},
[&] () {
_gpu_session.upgrade_ram(donate);
donate /= 4;
});
try {
Buffer * buffer = new (&_heap) Buffer(_buffer_registry, cap, size, _buffer_handles);
fn(buffer->handle);
} catch (...) {
_gpu_session.free_buffer(cap);
throw;
}
if (buffer)
fn(*buffer);
}
void _unmap_buffer(Buffer_handle &h)
void _unmap_buffer(Buffer &h)
{
_env.rm().detach(h.map_offset);
h.map_offset = 0;
_gpu_session.unmap_buffer(h.map_cap);
_gpu_session.unmap_buffer(h.id());
h.map_cap = Genode::Dataspace_capability();
_available_gtt_size += h.size;
}
int _free_buffer(Handle_id const &id)
int _free_buffer(Gpu::Buffer_id const id)
{
bool const handled = _apply_buffer(id, [&] (Buffer_handle &bh) {
if (bh.map_cap.valid())
_unmap_buffer(bh);
try {
_buffer_space.apply<Buffer>(id, [&] (Buffer &b) {
if (b.map_cap.valid())
_unmap_buffer(b);
if (bh.gpu_vaddr_valid) {
_gpu_session.unmap_buffer_ppgtt(bh.cap, bh.gpu_vaddr.addr);
bh.gpu_vaddr_valid = false;
}
_gpu_session.free_buffer(bh.cap);
Genode::destroy(&_heap, &bh);
});
if (!handled) {
if (b.gpu_vaddr_valid) {
_gpu_session.unmap_buffer_ppgtt(b.id(), b.gpu_vaddr.addr);
b.gpu_vaddr_valid = false;
}
Genode::destroy(&_heap, &b);
});
return 0;
} catch (Genode::Id_space<Buffer>::Unknown_id) {
Genode::error(__func__, ": invalid handle ", id.value);
Genode::sleep_forever();
return -1;
}
return handled ? 0 : -1;
}
Offset _map_buffer(Buffer_handle &bh)
Offset _map_buffer(Buffer &b)
{
Offset offset = 0;
if (bh.map_cap.valid()) {
offset = bh.map_offset;
if (b.map_cap.valid()) {
offset = b.map_offset;
return offset;
}
try {
_gpu_session.upgrade_ram(4096);
bh.map_cap = _gpu_session.map_buffer(bh.cap, true);
bh.map_offset = static_cast<Offset>(_env.rm().attach(bh.map_cap));
offset = bh.map_offset;
b.map_cap = _gpu_session.map_buffer(b.id(), true);
b.map_offset = static_cast<Offset>(_env.rm().attach(b.map_cap));
offset = b.map_offset;
_available_gtt_size -= bh.size;
_available_gtt_size -= b.size;
} catch (...) {
if (bh.map_cap.valid()) { _gpu_session.unmap_buffer(bh.map_cap); }
bh.map_cap = Genode::Dataspace_capability();
Genode::error("could not attach GEM buffer handle: ", bh.handle);
if (b.map_cap.valid())
_gpu_session.unmap_buffer(b.id());
b.map_cap = Genode::Dataspace_capability();
Genode::error("could not attach GEM buffer handle: ", b.id().value);
Genode::sleep_forever();
}
return offset;
}
Offset _map_buffer(Handle_id const &id)
Offset _map_buffer(Gpu::Buffer_id const id)
{
Offset offset = 0;
bool handled = _apply_buffer(id, [&] (Buffer_handle &bh) {
offset = _map_buffer(bh);
});
if (!handled) {
try {
_buffer_space.apply<Buffer>(id, [&] (Buffer &b) {
offset = _map_buffer(b);
});
} catch (Genode::Id_space<Buffer>::Unknown_id) {
Genode::error(__func__, ": invalid handle ", id.value);
Genode::sleep_forever();
}
return offset;
}
/*******************
** lookup buffer **
*******************/
template <typename FUNC>
bool _apply_buffer(Handle_id const &id, FUNC const &fn)
{
bool found = false;
_buffer_handles.apply<Buffer_handle>(id, [&](Buffer_handle &bh) {
fn(bh);
found = true;
});
return found;
}
/***************************
** execbuffer completion **
***************************/
@ -419,42 +403,46 @@ class Drm_call
uint64_t const size = (p->size + 0xfff) & ~0xfff;
_alloc_buffer(size, [&](Handle const &handle) {
p->size = size;
p->handle = handle.id().value;
try {
_alloc_buffer(size, [&](Buffer const &b) {
p->size = size;
p->handle = b.id().value;
if (verbose_ioctl) {
Genode::error(__func__, ": ", "handle: ", handle.id().value,
" size: ", size);
}
});
return 0;
if (verbose_ioctl) {
Genode::error(__func__, ": ", "handle: ", b.id().value,
" size: ", size);
}
});
return 0;
} catch (...) {
return -1;
}
}
int _device_gem_mmap(void *arg)
{
auto const p = reinterpret_cast<drm_i915_gem_mmap *>(arg);
Handle_id const handle { .value = p->handle };
Gpu::Buffer_id const id { .value = p->handle };
bool map_failed { true };
bool handled = _apply_buffer(handle, [&] (Buffer_handle &bh) {
if (bh.mmap(_env)) {
p->addr_ptr = bh.mmap_addr();
map_failed = false;
}
});
try {
_buffer_space.apply<Buffer>(id, [&] (Buffer &b) {
if (b.mmap(_env)) {
p->addr_ptr = b.mmap_addr();
map_failed = false;
}
});
} catch (Genode::Id_space<Buffer>::Unknown_id) { }
if (verbose_ioctl) {
Genode::error(__func__, ": ", "handle: ", handle,
!handled ? " buffer unknown" : "",
Genode::error(__func__, ": ", "handle: ", id,
map_failed ? " buffer inaccessible" : "",
" flags=", p->flags,
" addr=", Genode::Hex(p->addr_ptr));
}
if (!handled || map_failed)
if (map_failed)
return -1;
return 0;
@ -463,7 +451,7 @@ class Drm_call
int _device_gem_mmap_gtt(void *arg)
{
auto const p = reinterpret_cast<drm_i915_gem_mmap_gtt *>(arg);
Handle_id const id { .value = p->handle };
Gpu::Buffer_id const id { .value = p->handle };
if (verbose_ioctl) {
Genode::error(__func__, ": ", "handle: ", id.value,
@ -499,7 +487,7 @@ class Drm_call
{
/* XXX check read_domains/write_domain */
auto const p = reinterpret_cast<drm_i915_gem_set_domain*>(arg);
Handle_id const id { .value = p->handle };
Gpu::Buffer_id const id { .value = p->handle };
uint32_t const rd = p->read_domains;
uint32_t const wd = p->write_domain;
@ -610,7 +598,7 @@ class Drm_call
int _device_gem_set_tiling(void *arg)
{
auto const p = reinterpret_cast<drm_i915_gem_set_tiling*>(arg);
Handle_id const id { .value = p->handle };
Gpu::Buffer_id const id { .value = p->handle };
uint32_t const mode = p->tiling_mode;
uint32_t const stride = p->stride;
uint32_t const swizzle = p->swizzle_mode;
@ -623,21 +611,20 @@ class Drm_call
"swizzle: ", swizzle);
}
bool ok = false;
bool handled = _apply_buffer(id, [&] (Buffer_handle &bh) {
if (!bh.cap.valid())
return;
bool ok = false;
try {
_buffer_space.apply<Buffer>(id, [&] (Buffer &b) {
/* we need a valid GGTT mapping for fencing */
if (!bh.map_cap.valid() && !_map_buffer(bh))
return;
/* we need a valid GGTT mapping for fencing */
if (!b.map_cap.valid() && !_map_buffer(b))
return;
uint32_t const m = (stride << 16) | (mode == 1 ? 1 : 0);
ok = _gpu_session.set_tiling(bh.map_cap, m);
});
if (!handled)
uint32_t const m = (stride << 16) | (mode == 1 ? 1 : 0);
ok = _gpu_session.set_tiling(b.id(), m);
});
} catch (Genode::Id_space<Buffer>::Unknown_id) {
Genode::error(__func__, ": invalid handle: ", id.value);
}
return ok ? 0 : -1;
}
@ -657,7 +644,7 @@ class Drm_call
/* batch-buffer index and cap */
unsigned const bb_id = (p->flags & I915_EXEC_BATCH_FIRST) ? 0 : p->buffer_count - 1;
Buffer_handle *command_buffer = nullptr;
Buffer *command_buffer = nullptr;
if (verbose_ioctl) {
uint64_t const ctx_id = p->rsvd1;
@ -722,38 +709,40 @@ class Drm_call
return -1;
}
int ret = -1;
Handle_id const id { .value = obj[i].handle };
int ret = -1;
Gpu::Buffer_id const id { .value = obj[i].handle };
bool handled = _apply_buffer(id, [&](Buffer_handle &bh) {
if (!bh.valid())
return;
try {
_buffer_space.apply<Buffer>(id, [&](Buffer &b) {
if (bh.busy)
Genode::warning("handle: ", obj[i].handle, " reused but is busy");
if (b.busy)
Genode::warning("handle: ", obj[i].handle, " reused but is busy");
if (bh.gpu_vaddr_valid && bh.gpu_vaddr.addr != obj[i].offset) {
Genode::error("unmap already mapped ", bh.handle, " ", Genode::Hex(bh.gpu_vaddr.addr), "->", Genode::Hex(obj[i].offset));
_unmap_buffer_ppgtt(bh);
}
if (b.gpu_vaddr_valid && b.gpu_vaddr.addr != obj[i].offset) {
Genode::error("unmap already mapped ", b.id().value, " ",
Genode::Hex(b.gpu_vaddr.addr), "->",
Genode::Hex(obj[i].offset));
_unmap_buffer_ppgtt(b);
}
if (!bh.gpu_vaddr_valid)
_map_buffer_ppgtt(bh, Gpu_virtual_address { .addr = obj[i].offset });
if (!b.gpu_vaddr_valid)
_map_buffer_ppgtt(b, Gpu_virtual_address { .addr = obj[i].offset });
if (!bh.gpu_vaddr_valid) {
Genode::error("handle: ", obj[i].handle, " gpu_vaddr invalid");
return;
}
if (!b.gpu_vaddr_valid) {
Genode::error("handle: ", obj[i].handle, " gpu_vaddr invalid");
return;
}
bh.busy = true;
b.busy = true;
if (i == bb_id)
command_buffer = &bh;
if (i == bb_id)
command_buffer = &b;
ret = 0;
});
ret = 0;
});
} catch (Genode::Id_space<Buffer>::Unknown_id) { }
if (!handled || ret) {
if (ret) {
Genode::error("handle: ", obj[i].handle, " invalid, ret=", ret);
return ret;
}
@ -762,13 +751,13 @@ class Drm_call
if (!command_buffer)
return -1;
command_buffer->seqno = _gpu_session.exec_buffer(command_buffer->cap,
command_buffer->seqno = _gpu_session.exec_buffer(command_buffer->id(),
p->batch_len);
for (uint64_t i = 0; i < p->buffer_count; i++) {
Handle_id const id { .value = obj[i].handle };
_apply_buffer(id, [&](Buffer_handle &bh) {
bh.seqno = command_buffer->seqno;
Gpu::Buffer_id const id { .value = obj[i].handle };
_buffer_space.apply<Buffer>(id, [&](Buffer &b) {
b.seqno = command_buffer->seqno;
});
}
@ -777,7 +766,7 @@ class Drm_call
* of signal ep, the original drm_i915_gem_wait simply 0 now
*/
struct drm_i915_gem_wait wait = {
.bo_handle = (__u32)command_buffer->handle.id().value,
.bo_handle = (__u32)command_buffer->id().value,
.flags = 0,
.timeout_ns = -1LL
};
@ -789,13 +778,16 @@ class Drm_call
int _device_gem_busy(void *arg)
{
auto const p = reinterpret_cast<drm_i915_gem_busy*>(arg);
Handle_id const id { .value = p->handle };
Gpu::Buffer_id const id { .value = p->handle };
bool handled = _apply_buffer(id, [&](Buffer_handle const &bh) {
p->busy = bh.busy;
});
return handled ? 0 : -1;
try {
_buffer_space.apply<Buffer>(id, [&](Buffer const &b) {
p->busy = b.busy;
});
return 0;
} catch (Genode::Id_space<Buffer>::Unknown_id) {
return -1;
}
}
int _device_gem_madvise(void *arg)
@ -811,16 +803,17 @@ class Drm_call
int _device_gem_wait(void *arg)
{
auto const p = reinterpret_cast<drm_i915_gem_wait*>(arg);
Handle_id const id { .value = p->bo_handle };
Gpu::Buffer_id const id { .value = p->bo_handle };
bool busy = true;
while (busy) {
bool handled = _apply_buffer(id, [&](Buffer_handle &bh) {
busy = bh.busy;
});
if (!handled) {
try {
_buffer_space.apply<Buffer>(id, [&](Buffer &b) {
busy = b.busy;
});
} catch (Genode::Id_space<Buffer>::Unknown_id) {
Genode::error(__func__, ": handle ", p->bo_handle, " invalid");
return -1;
}
@ -892,7 +885,7 @@ class Drm_call
int _generic_gem_close(void *arg)
{
auto const p = reinterpret_cast<drm_gem_close*>(arg);
Handle_id const id { .value = p->handle };
Gpu::Buffer_id const id { .value = p->handle };
return _free_buffer(id);
}
@ -993,7 +986,7 @@ class Drm_call
}
int const prime_fd { 44 };
Handle_id prime_handle { };
Gpu::Buffer_id prime_handle { };
int _generic_prime_fd_to_handle(void *arg)
{
@ -1010,16 +1003,18 @@ class Drm_call
{
auto const p = reinterpret_cast<drm_prime_handle *>(arg);
Handle_id const handle { .value = p->handle };
bool handled = _apply_buffer(handle, [&](Buffer_handle const &bh) {
if (!prime_handle.value)
prime_handle = handle;
Gpu::Buffer_id const id { .value = p->handle };
try {
_buffer_space.apply<Buffer>(id, [&](Buffer const &b) {
if (!prime_handle.value)
prime_handle = id;
if (prime_handle.value != handle.value)
Genode::error("prime handle changed - ignored ", bh.handle);
});
if (!handled)
if (prime_handle.value != id.value)
Genode::error("prime handle changed - ignored ", b.id().value);
});
} catch (Genode::Id_space<Buffer>::Unknown_id) {
return -1;
}
p->fd = prime_fd;
return 0;
@ -1069,7 +1064,7 @@ class Drm_call
{
bool result = false;
_buffer_registry.for_each([&] (Buffer_handle &h) {
_buffer_space.for_each<Buffer>([&] (Buffer &h) {
if (h.map_offset != offset) { return; }
if (length > h.size) { Genode::error("map_buffer_ggtt: size mismatch"); return; }
result = true;
@ -1085,20 +1080,20 @@ class Drm_call
{
bool found = false;
_buffer_registry.for_each([&] (Buffer_handle &bh) {
if (found || !bh.buffer_attached.constructed())
_buffer_space.for_each<Buffer>([&] (Buffer &b) {
if (found || !b.buffer_attached.constructed())
return;
if (reinterpret_cast<void *>(bh.mmap_addr()) != addr)
if (reinterpret_cast<void *>(b.mmap_addr()) != addr)
return;
if (bh.buffer_attached->size() != length) {
if (b.buffer_attached->size() != length) {
Genode::warning(__func__, " size mismatch");
Genode::sleep_forever();
return;
}
bh.buffer_attached.destruct();
b.buffer_attached.destruct();
found = true;
});
@ -1115,7 +1110,7 @@ class Drm_call
bool handled = false;
_buffer_registry.for_each([&] (Buffer_handle &h) {
_buffer_space.for_each<Buffer>([&] (Buffer &h) {
if (handled) return;
if (h.map_offset != offset) return;
if (length > h.size) { Genode::error("unmap_buffer_ggtt: size mismatch"); return; }
@ -1152,7 +1147,7 @@ class Drm_call
/* make done buffer objects */
Gpu::Info gpu_info { _gpu_session.info() };
_buffer_registry.for_each([&] (Buffer_handle &h) {
_buffer_space.for_each<Buffer>([&] (Buffer &h) {
if (!h.busy) return;
if (h.seqno.id > gpu_info.last_completed.id) return;
h.busy = false;

View File

@ -40,35 +40,34 @@ class Gpu::Session_client : public Genode::Rpc_client<Session>
Info info() const override {
return call<Rpc_info>(); }
Gpu::Info::Execution_buffer_sequence exec_buffer(Genode::Dataspace_capability cap,
Gpu::Info::Execution_buffer_sequence exec_buffer(Buffer_id id,
Genode::size_t size) override {
return call<Rpc_exec_buffer>(cap, size); }
return call<Rpc_exec_buffer>(id, size); }
void completion_sigh(Genode::Signal_context_capability sigh) override {
call<Rpc_completion_sigh>(sigh); }
Genode::Dataspace_capability alloc_buffer(Genode::size_t size) override {
return call<Rpc_alloc_buffer>(size); }
Genode::Dataspace_capability alloc_buffer(Buffer_id id, Genode::size_t size) override {
return call<Rpc_alloc_buffer>(id, size); }
void free_buffer(Genode::Dataspace_capability ds) override {
call<Rpc_free_buffer>(ds); }
void free_buffer(Gpu::Buffer_id id) override {
call<Rpc_free_buffer>(id); }
Genode::Dataspace_capability map_buffer(Genode::Dataspace_capability ds,
Genode::Dataspace_capability map_buffer(Buffer_id id,
bool aperture) override {
return call<Rpc_map_buffer>(ds, aperture); }
return call<Rpc_map_buffer>(id, aperture); }
void unmap_buffer(Genode::Dataspace_capability ds) override {
call<Rpc_unmap_buffer>(ds); }
void unmap_buffer(Buffer_id id) override {
call<Rpc_unmap_buffer>(id); }
bool map_buffer_ppgtt(Genode::Dataspace_capability ds,
Gpu::addr_t va) override {
return call<Rpc_map_buffer_ppgtt>(ds, va); }
bool map_buffer_ppgtt(Buffer_id id, Gpu::addr_t va) override {
return call<Rpc_map_buffer_ppgtt>(id, va); }
void unmap_buffer_ppgtt(Genode::Dataspace_capability ds, Gpu::addr_t va) override {
call<Rpc_unmap_buffer_ppgtt>(ds, va); }
void unmap_buffer_ppgtt(Buffer_id id, Gpu::addr_t va) override {
call<Rpc_unmap_buffer_ppgtt>(id, va); }
bool set_tiling(Genode::Dataspace_capability ds, unsigned mode) override {
return call<Rpc_set_tiling>(ds, mode); }
bool set_tiling(Buffer_id id, unsigned mode) override {
return call<Rpc_set_tiling>(id, mode); }
};
#endif /* _INCLUDE__GPU_SESSION__CLIENT_H_ */

View File

@ -14,12 +14,16 @@
#ifndef _INCLUDE__GPU_SESSION__GPU_SESSION_H_
#define _INCLUDE__GPU_SESSION__GPU_SESSION_H_
#include <base/id_space.h>
#include <session/session.h>
namespace Gpu {
using addr_t = Genode::uint64_t;
struct Buffer;
using Buffer_id = Genode::Id_space<Buffer>::Id;
struct Info;
struct Session;
}
@ -74,9 +78,10 @@ struct Gpu::Info
*/
struct Gpu::Session : public Genode::Session
{
struct Out_of_ram : Genode::Exception { };
struct Out_of_caps : Genode::Exception { };
struct Invalid_state : Genode::Exception { };
struct Out_of_ram : Genode::Exception { };
struct Out_of_caps : Genode::Exception { };
struct Invalid_state : Genode::Exception { };
struct Conflicting_id : Genode::Exception { };
struct Mapping_buffer_failed : Genode::Exception { };
enum { REQUIRED_QUOTA = 1024 * 1024, CAP_QUOTA = 8, };
@ -97,14 +102,14 @@ struct Gpu::Session : public Genode::Session
/**
* Execute commands from given buffer
*
* \param cap capability to buffer object containing the exec buffer
* \param id buffer id
* \param size size of the batch buffer in bytes
*
* \return execution buffer sequence number for complete checks
*
* \throw Invalid_state is thrown if the provided buffer is not valid, e.g not mapped
*/
virtual Gpu::Info::Execution_buffer_sequence exec_buffer(Genode::Dataspace_capability cap, Genode::size_t size) = 0;
virtual Gpu::Info::Execution_buffer_sequence exec_buffer(Buffer_id id, Genode::size_t size) = 0;
/**
* Register completion signal handler
@ -117,65 +122,66 @@ struct Gpu::Session : public Genode::Session
/**
* Allocate buffer dataspace
*
* \param id buffer id to be associated with the buffer
* \param size size of buffer in bytes
*
* \throw Out_of_ram
* \throw Out_of_caps
* \throw Conflicting_id
*/
virtual Genode::Dataspace_capability alloc_buffer(Genode::size_t size) = 0;
virtual Genode::Dataspace_capability alloc_buffer(Buffer_id id, Genode::size_t size) = 0;
/**
* Free buffer dataspace
*
* \param ds dataspace capability for buffer
*/
virtual void free_buffer(Genode::Dataspace_capability ds) = 0;
virtual void free_buffer(Buffer_id id) = 0;
/**
* Map buffer
*
* \param ds dataspace capability for buffer
* \param id buffer id
* \param aperture if true create CPU accessible mapping through
* GGTT window, otherwise create PPGTT mapping
*
* \throw Mapping_buffer_failed
*/
virtual Genode::Dataspace_capability map_buffer(Genode::Dataspace_capability ds,
virtual Genode::Dataspace_capability map_buffer(Buffer_id id,
bool aperture) = 0;
/**
* Unmap buffer
*
* \param ds dataspace capability for buffer
* \param id buffer id
*/
virtual void unmap_buffer(Genode::Dataspace_capability ds) = 0;
virtual void unmap_buffer(Buffer_id id) = 0;
/**
* Map buffer in PPGTT
*
* \param ds dataspace capability for buffer
* \param id buffer id
* \param va virtual address
*
* \throw Mapping_buffer_failed
* \throw Out_of_ram
*/
virtual bool map_buffer_ppgtt(Genode::Dataspace_capability ds,
Gpu::addr_t va) = 0;
virtual bool map_buffer_ppgtt(Buffer_id id, Gpu::addr_t va) = 0;
/**
* Unmap buffer
*
* \param ds dataspace capability for buffer
* \param id buffer id
*/
virtual void unmap_buffer_ppgtt(Genode::Dataspace_capability ds, Gpu::addr_t) = 0;
virtual void unmap_buffer_ppgtt(Buffer_id id, Gpu::addr_t) = 0;
/**
* Set tiling for buffer
*
* \param ds dataspace capability for buffer
* \param id buffer id
* \param mode tiling mode
*/
virtual bool set_tiling(Genode::Dataspace_capability ds, unsigned mode) = 0;
virtual bool set_tiling(Buffer_id id, unsigned mode) = 0;
/*******************
** RPC interface **
@ -184,25 +190,25 @@ struct Gpu::Session : public Genode::Session
GENODE_RPC(Rpc_info, Info, info);
GENODE_RPC_THROW(Rpc_exec_buffer, Gpu::Info::Execution_buffer_sequence, exec_buffer,
GENODE_TYPE_LIST(Invalid_state),
Genode::Dataspace_capability, Genode::size_t);
Gpu::Buffer_id, Genode::size_t);
GENODE_RPC(Rpc_completion_sigh, void, completion_sigh,
Genode::Signal_context_capability);
GENODE_RPC_THROW(Rpc_alloc_buffer, Genode::Dataspace_capability, alloc_buffer,
GENODE_TYPE_LIST(Out_of_ram),
Genode::size_t);
GENODE_RPC(Rpc_free_buffer, void, free_buffer, Genode::Dataspace_capability);
Gpu::Buffer_id, Genode::size_t);
GENODE_RPC(Rpc_free_buffer, void, free_buffer, Gpu::Buffer_id);
GENODE_RPC_THROW(Rpc_map_buffer, Genode::Dataspace_capability, map_buffer,
GENODE_TYPE_LIST(Mapping_buffer_failed, Out_of_ram),
Genode::Dataspace_capability, bool);
Gpu::Buffer_id, bool);
GENODE_RPC(Rpc_unmap_buffer, void, unmap_buffer,
Genode::Dataspace_capability);
Gpu::Buffer_id);
GENODE_RPC_THROW(Rpc_map_buffer_ppgtt, bool, map_buffer_ppgtt,
GENODE_TYPE_LIST(Mapping_buffer_failed, Out_of_ram),
Genode::Dataspace_capability, Gpu::addr_t);
Gpu::Buffer_id, Gpu::addr_t);
GENODE_RPC(Rpc_unmap_buffer_ppgtt, void, unmap_buffer_ppgtt,
Genode::Dataspace_capability, Gpu::addr_t);
Gpu::Buffer_id, Gpu::addr_t);
GENODE_RPC(Rpc_set_tiling, bool, set_tiling,
Genode::Dataspace_capability, unsigned);
Gpu::Buffer_id, unsigned);
GENODE_RPC_INTERFACE(Rpc_info, Rpc_exec_buffer,
Rpc_completion_sigh, Rpc_alloc_buffer,

View File

@ -1462,6 +1462,7 @@ class Gpu::Session_component : public Genode::Session_object<Gpu::Session>
struct Buffer
{
Gpu::Buffer_id const id;
Genode::Dataspace_capability cap;
Gpu::addr_t ppgtt_va { };
@ -1472,13 +1473,26 @@ class Gpu::Session_component : public Genode::Session_object<Gpu::Session>
Igd::Ggtt::Mapping map { };
Buffer(Genode::Dataspace_capability cap) : cap(cap) { }
Buffer(Gpu::Buffer_id id, Genode::Dataspace_capability cap)
: id { id }, cap { cap } { }
virtual ~Buffer() { }
};
Genode::Registry<Genode::Registered<Buffer>> _buffer_registry { };
template <typename FN>
void _apply_buffer(Gpu::Buffer_id id, FN const &fn)
{
_buffer_registry.for_each([&] (Buffer &buffer) {
if (id.value != buffer.id.value) {
return;
}
fn(buffer);
});
}
Genode::uint64_t seqno { 0 };
void _free_buffers()
@ -1559,13 +1573,12 @@ class Gpu::Session_component : public Genode::Session_object<Gpu::Session>
_device._subslices);
}
Gpu::Info::Execution_buffer_sequence exec_buffer(Genode::Dataspace_capability cap,
Gpu::Info::Execution_buffer_sequence exec_buffer(Buffer_id id,
Genode::size_t) override
{
bool found = false;
_buffer_registry.for_each([&] (Buffer &buffer) {
if (found || !(buffer.cap == cap)) { return; }
_apply_buffer(id, [&] (Buffer &buffer) {
if (!buffer.ppgtt_va_valid) {
Genode::error("Invalid execbuffer");
@ -1588,8 +1601,15 @@ class Gpu::Session_component : public Genode::Session_object<Gpu::Session>
_vgpu.completion_sigh(sigh);
}
Genode::Dataspace_capability alloc_buffer(Genode::size_t size) override
Genode::Dataspace_capability alloc_buffer(Gpu::Buffer_id id,
Genode::size_t size) override
{
bool found = false;
_apply_buffer(id, [&] (Buffer &) {
found = true;
});
if (found) { throw Conflicting_id(); }
/*
* XXX allocator overhead is not
* included, mapping costs are not included and we throw at
@ -1605,7 +1625,7 @@ class Gpu::Session_component : public Genode::Session_object<Gpu::Session>
Genode::Dataspace_capability cap = _device.alloc_buffer(_heap, size);
try {
new (&_heap) Genode::Registered<Buffer>(_buffer_registry, cap);
new (&_heap) Genode::Registered<Buffer>(_buffer_registry, id, cap);
} catch (...) {
if (cap.valid())
_device.free_buffer(_heap, cap);
@ -1619,33 +1639,27 @@ class Gpu::Session_component : public Genode::Session_object<Gpu::Session>
return Genode::Dataspace_capability();
}
void free_buffer(Genode::Dataspace_capability cap) override
void free_buffer(Gpu::Buffer_id id) override
{
if (!cap.valid()) { return; }
auto lookup_and_free = [&] (Buffer &buffer) {
if (!(buffer.cap == cap)) { return; }
if (buffer.map.offset != Igd::Ggtt::Mapping::INVALID_OFFSET) {
Genode::error("cannot free mapped buffer");
/* XXX throw */
}
_device.free_buffer(_heap, cap);
_device.free_buffer(_heap, buffer.cap);
Genode::destroy(&_heap, &buffer);
};
_buffer_registry.for_each(lookup_and_free);
_apply_buffer(id, lookup_and_free);
}
Genode::Dataspace_capability map_buffer(Genode::Dataspace_capability cap,
Genode::Dataspace_capability map_buffer(Gpu::Buffer_id id,
bool aperture) override
{
if (!cap.valid()) { return Genode::Dataspace_capability(); }
Genode::Dataspace_capability map_cap;
auto lookup_and_map = [&] (Buffer &buffer) {
if (!(buffer.cap == cap)) { return; }
if (buffer.map.offset != Igd::Ggtt::Mapping::INVALID_OFFSET) {
Genode::error("buffer already mapped");
@ -1653,7 +1667,8 @@ class Gpu::Session_component : public Genode::Session_object<Gpu::Session>
}
try {
Igd::Ggtt::Mapping const &map = _device.map_buffer(_heap, cap, aperture);
Igd::Ggtt::Mapping const &map =
_device.map_buffer(_heap, buffer.cap, aperture);
buffer.map.cap = map.cap;
buffer.map.offset = map.offset;
map_cap = buffer.map.cap;
@ -1662,19 +1677,17 @@ class Gpu::Session_component : public Genode::Session_object<Gpu::Session>
throw Gpu::Session::Out_of_ram();
}
};
_buffer_registry.for_each(lookup_and_map);
_apply_buffer(id, lookup_and_map);
return map_cap;
}
void unmap_buffer(Genode::Dataspace_capability cap) override
void unmap_buffer(Gpu::Buffer_id id) override
{
if (!cap.valid()) { return; }
bool unmapped = false;
auto lookup_and_unmap = [&] (Buffer &buffer) {
if (!(buffer.map.cap == cap)) { return; }
if (!buffer.map.cap.valid()) { return; }
if (buffer.fenced != Buffer::INVALID_FENCE) {
_device.clear_tiling(buffer.fenced);
@ -1685,20 +1698,16 @@ class Gpu::Session_component : public Genode::Session_object<Gpu::Session>
buffer.map.offset = Igd::Ggtt::Mapping::INVALID_OFFSET;
unmapped = true;
};
_buffer_registry.for_each(lookup_and_unmap);
_apply_buffer(id, lookup_and_unmap);
if (!unmapped) { Genode::error("buffer not mapped"); }
}
bool map_buffer_ppgtt(Genode::Dataspace_capability cap,
Gpu::addr_t va) override
bool map_buffer_ppgtt(Gpu::Buffer_id id, Gpu::addr_t va) override
{
if (!cap.valid()) { return false; }
enum { ALLOC_FAILED, MAP_FAILED, OK } result = ALLOC_FAILED;
auto lookup_and_map = [&] (Buffer &buffer) {
if (!(buffer.cap == cap)) { return; }
if (buffer.ppgtt_va_valid) {
Genode::error("buffer already mapped");
@ -1706,7 +1715,7 @@ class Gpu::Session_component : public Genode::Session_object<Gpu::Session>
}
try {
Genode::Dataspace_client buf(cap);
Genode::Dataspace_client buf(buffer.cap);
/* XXX check that actual_size matches alloc_buffer size */
Genode::size_t const actual_size = buf.size();
Genode::addr_t const phys_addr = buf.phys_addr();
@ -1724,7 +1733,7 @@ class Gpu::Session_component : public Genode::Session_object<Gpu::Session>
return;
}
};
_buffer_registry.for_each(lookup_and_map);
_apply_buffer(id, lookup_and_map);
switch (result) {
case ALLOC_FAILED: throw Gpu::Session::Out_of_ram();
@ -1735,16 +1744,10 @@ class Gpu::Session_component : public Genode::Session_object<Gpu::Session>
}
}
void unmap_buffer_ppgtt(Genode::Dataspace_capability cap,
void unmap_buffer_ppgtt(Gpu::Buffer_id id,
Gpu::addr_t va) override
{
if (!cap.valid()) {
Genode::error("invalid buffer capability");
return;
}
auto lookup_and_unmap = [&] (Buffer &buffer) {
if (!(buffer.cap == cap)) { return; }
if (!buffer.ppgtt_va_valid) {
Genode::error("buffer not mapped");
@ -1756,15 +1759,15 @@ class Gpu::Session_component : public Genode::Session_object<Gpu::Session>
return;
}
Genode::Dataspace_client buf(cap);
Genode::Dataspace_client buf(buffer.cap);
Genode::size_t const actual_size = buf.size();
_vgpu.rcs_unmap_ppgtt(va, actual_size);
buffer.ppgtt_va_valid = false;
};
_buffer_registry.for_each(lookup_and_unmap);
_apply_buffer(id, lookup_and_unmap);
}
bool set_tiling(Genode::Dataspace_capability cap,
bool set_tiling(Gpu::Buffer_id id,
Genode::uint32_t const mode) override
{
if (_vgpu.active_fences > Igd::Device::Vgpu::MAX_FENCES) {
@ -1774,10 +1777,10 @@ class Gpu::Session_component : public Genode::Session_object<Gpu::Session>
Buffer *b = nullptr;
auto lookup = [&] (Buffer &buffer) {
if (!(buffer.map.cap == cap)) { return; }
if (!buffer.map.cap.valid()) { return; }
b = &buffer;
};
_buffer_registry.for_each(lookup);
_apply_buffer(id, lookup);
if (b == nullptr) {
Genode::error("attempt to set tiling for non-mapped buffer");