vfs_gpu: VFS plugin that handles GPU completions

When a <gpu> node is present in the VFS, the plugin opens a
Gpu::Connection for each open call to the 'gpu' node and installs a
completion signal handler. A read only to the fd returned only comes
back if a completion signal has been received between two read
transctions to the fd.

For now the Gpu::Connections can be retrieved by calling the
'vfs_gpu_connection(unsigned long id)' function. The id can be obtained
using 'stat' on the 'gpu' device and is located in the inode (st_ino)
field of the stat buffer.

issue #4380
This commit is contained in:
Sebastian Sumpf 2021-10-14 08:59:48 +02:00 committed by Christian Helmuth
parent b7d25636e5
commit b3e12dcece
6 changed files with 206 additions and 0 deletions

View File

@ -0,0 +1,6 @@
SRC_CC = vfs.cc
vpath %.cc $(REP_DIR)/src/lib/vfs/gpu
SHARED_LIB = yes
CC_CXX_WARN_STRICT =

View File

@ -0,0 +1,9 @@
MIRROR_FROM_REP_DIR := lib/mk/vfs_gpu.mk src/lib/vfs/gpu
content: $(MIRROR_FROM_REP_DIR) LICENSE
$(MIRROR_FROM_REP_DIR):
$(mirror_from_rep_dir)
LICENSE:
cp $(GENODE_DIR)/LICENSE $@

View File

@ -0,0 +1 @@
2021-11-25 5b57237a277a78a2c4cca4afb8caf919d5ef79b3

View File

@ -0,0 +1,6 @@
base
os
so
vfs
gems
gpu_session

View File

@ -0,0 +1 @@
LIBS = vfs_gpu

View File

@ -0,0 +1,183 @@
/*
* \brief Minimal file system for GPU session
* \author Sebastian Sumpf
* \date 2021-10-14
*
* The file system only handles completion signals of the GPU session in order
* to work from non-EP threads (i.e., pthreads) in libc components. A read
* returns only in case a completion signal has been delivered since the
* previous call to read.
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#include <gpu_session/connection.h>
#include <os/vfs.h>
#include <util/xml_generator.h>
#include <vfs/single_file_system.h>
namespace Vfs_gpu
{
using namespace Vfs;
using namespace Genode;
struct File_system;
}
struct Vfs_gpu::File_system : Single_file_system
{
struct Gpu_vfs_handle : Single_vfs_handle
{
bool _complete { false };
Genode::Env &_env;
Gpu::Connection _gpu_session { _env };
Genode::Io_signal_handler<Gpu_vfs_handle> _completion_sigh {
_env.ep(), *this, &Gpu_vfs_handle::_handle_completion };
using Id_space = Genode::Id_space<Gpu_vfs_handle>;
Id_space::Element const _elem;
void _handle_completion()
{
_complete = true;
io_progress_response();
}
Gpu_vfs_handle(Genode::Env &env,
Directory_service &ds,
File_io_service &fs,
Genode::Allocator &alloc,
Id_space &space)
: Single_vfs_handle(ds, fs, alloc, 0),
_env(env), _elem(*this, space)
{
_gpu_session.completion_sigh(_completion_sigh);
}
Read_result read(char *dst, file_size /* count */,
file_size &out_count) override
{
if (!_complete) return READ_QUEUED;
_complete = false;
dst[0] = 1;
out_count = 1;
return READ_OK;
}
Write_result write(char const *, file_size, file_size &) override
{
return WRITE_ERR_IO;
}
bool read_ready() override { return _complete; }
Id_space::Id id() const { return _elem.id(); }
};
Vfs::Env &_env;
typedef String<32> Config;
Id_space<Gpu_vfs_handle> _handle_space { };
Id_space<Gpu_vfs_handle>::Id _last_id { .value = ~0ul };
File_system(Vfs::Env &env, Xml_node config)
:
Single_file_system(Node_type::CONTINUOUS_FILE,
type_name(),
Node_rwx::ro(),
config),
_env(env)
{ }
Open_result open(char const *path, unsigned,
Vfs::Vfs_handle **out_handle,
Allocator &alloc) override
{
if (!_single_file(path))
return OPEN_ERR_UNACCESSIBLE;
try {
Gpu_vfs_handle *handle = new (alloc)
Gpu_vfs_handle(_env.env(), *this, *this, alloc, _handle_space);
_last_id = handle->id();
*out_handle = handle;
return OPEN_OK;
}
catch (Genode::Out_of_ram) { return OPEN_ERR_OUT_OF_RAM; }
catch (Genode::Out_of_caps) { return OPEN_ERR_OUT_OF_CAPS; }
}
Stat_result stat(char const *path, Stat &out) override
{
if (!_single_file(path))
return STAT_ERR_NO_ENTRY;
out.inode = (unsigned long)_last_id.value;
return STAT_OK;
}
static char const *type_name() { return "gpu"; }
char const *type() override { return type_name(); }
};
static Vfs_gpu::File_system *_fs { nullptr };
/**
* XXX: return GPU session for given ID, retrieved by 'stat->inode'.
* This function is used, for example, by libdrm via (dlopen/dlsym).
*/
Gpu::Connection *vfs_gpu_connection(unsigned long id)
{
if (!_fs) return nullptr;
using Gpu_vfs_handle = Vfs_gpu::File_system::Gpu_vfs_handle;
using Id_space = Genode::Id_space<Gpu_vfs_handle>;
try {
return _fs->_handle_space.apply<Gpu_vfs_handle>(
Id_space::Id { .value = id },
[] (Gpu_vfs_handle &handle) {
return &handle._gpu_session; });
} catch (...) { }
return nullptr;
}
/**************************
** VFS plugin interface **
**************************/
extern "C" Vfs::File_system_factory *vfs_file_system_factory(void)
{
struct Factory : Vfs::File_system_factory
{
Vfs::File_system *create(Vfs::Env &vfs_env,
Genode::Xml_node node) override
{
try {
_fs = new (vfs_env.alloc()) Vfs_gpu::File_system(vfs_env, node);
return _fs;
}
catch (...) { Genode::error("could not create 'gpu_fs' "); }
return nullptr;
}
};
static Factory factory;
return &factory;
}