Skip to content

Commit 82d0c03

Browse files
committed
feat: host NVIDIA driver fallback
1 parent b30b7dd commit 82d0c03

16 files changed

Lines changed: 2978 additions & 5 deletions

File tree

apps/ll-cli/src/main.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,44 @@ void addInspectCommand(CLI::App &commandParser,
607607
->check(validatorString);
608608
}
609609

610+
// Function to add the extension subcommand
611+
void addExtensionCommand(CLI::App &commandParser,
612+
ExtensionOptions &extensionOptions,
613+
const std::string &group)
614+
{
615+
auto *cliExtension =
616+
commandParser.add_subcommand("extension", _("Manage extension overrides"))
617+
->group(group)
618+
->usage(_("Usage: ll-cli extension SUBCOMMAND [OPTIONS]"));
619+
620+
cliExtension->require_subcommand(1);
621+
622+
auto *cliImportCdi =
623+
cliExtension
624+
->add_subcommand("import-cdi", _("Import CDI rules into extension overrides"))
625+
->usage(_("Usage: ll-cli extension import-cdi [OPTIONS]"));
626+
cliImportCdi
627+
->add_option("--name",
628+
extensionOptions.name,
629+
_("Specify extension name prefix to update"))
630+
->type_name("NAME")
631+
->check(validatorString);
632+
cliImportCdi
633+
->add_option("--config",
634+
extensionOptions.configPath,
635+
_("Specify config path (default: ~/.config/linglong/config.json)"))
636+
->type_name("FILE");
637+
cliImportCdi
638+
->add_option("--cdi",
639+
extensionOptions.cdiPath,
640+
_("Specify CDI JSON path (default: nvidia-ctk cdi generate --format json)"))
641+
->type_name("FILE");
642+
cliImportCdi
643+
->add_flag("--apply-when-installed",
644+
extensionOptions.applyWhenInstalled,
645+
_("Apply overrides even when extension is installed"));
646+
}
647+
610648
} // namespace
611649

612650
using namespace linglong::utils::global;
@@ -690,12 +728,14 @@ You can report bugs to the linyaps team under this project: https://github.com/O
690728
ContentOptions contentOptions{};
691729
RepoOptions repoOptions{};
692730
InspectOptions inspectOptions{};
731+
ExtensionOptions extensionOptions{};
693732

694733
// groups for subcommands
695734
auto *CliBuildInGroup = _("Managing installed applications and runtimes");
696735
auto *CliAppManagingGroup = _("Managing running applications");
697736
auto *CliSearchGroup = _("Finding applications and runtimes");
698737
auto *CliRepoGroup = _("Managing remote repositories");
738+
auto *CliExtensionGroup = _("Managing extensions");
699739

700740
// add all subcommands using the new functions
701741
addRunCommand(commandParser, runOptions, CliAppManagingGroup);
@@ -712,6 +752,7 @@ You can report bugs to the linyaps team under this project: https://github.com/O
712752
addContentCommand(commandParser, contentOptions, CliBuildInGroup);
713753
addPruneCommand(commandParser, CliAppManagingGroup);
714754
addInspectCommand(commandParser, inspectOptions, CliHiddenGroup);
755+
addExtensionCommand(commandParser, extensionOptions, CliExtensionGroup);
715756

716757
auto res = transformOldExec(argc, argv);
717758
CLI11_PARSE(commandParser, std::move(res));
@@ -920,6 +961,8 @@ You can report bugs to the linyaps team under this project: https://github.com/O
920961
result = cli->inspect(*ret, inspectOptions);
921962
} else if (name == "repo") {
922963
result = cli->repo(*ret, repoOptions);
964+
} else if (name == "extension") {
965+
result = cli->extension(*ret, extensionOptions);
923966
} else {
924967
// if subcommand name is not found, print help
925968
std::cout << commandParser.help("", CLI::AppFormatMode::All);

docs/pages/en/guide/reference/driver.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,6 @@ The base that applications depend on already includes the appropriate version of
2020

2121
Drivers not included in the base that require additional installation:
2222

23-
- NVIDIA proprietary drivers: Install via `sudo ll-cli install org.deepin.driver.display.nvidia.570-124-04`. The `570-124-04` is the driver version number, which must match the driver version installed on the host system. Check the host driver version through the `/sys/module/nvidia/version` file.
23+
- NVIDIA proprietary drivers: Install via `sudo ll-cli install org.deepin.driver.display.nvidia.570-124-04`. The `570-124-04` is the driver version number, which must match the driver version installed on the host system. Check the host driver version through the `/sys/module/nvidia/version` file. If the extension is not installed, linyaps will attempt to link NVIDIA driver files from the host at runtime.
2424
- Glenfly graphics drivers: Install via `sudo ll-cli install com.glenfly.driver.display.arise`.
2525
- Intel video codec drivers (VAAPI): Install via `sudo ll-cli install org.deepin.driver.media.intel`, which includes support for both new and legacy Intel graphics cards.

docs/pages/guide/reference/driver.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,6 @@
2020

2121
不在 base 中携带的,需要额外安装的驱动:
2222

23-
- 英伟达闭源驱动,通过 `sudo ll-cli install org.deepin.driver.display.nvidia.570-124-04` 安装。其中 `570-124-04` 是驱动版本号,需要与宿主机安装的驱动版本匹配,通过宿主机 `/sys/module/nvidia/version` 文件查看宿主机驱动的版本。
23+
- 英伟达闭源驱动,推荐通过 `sudo ll-cli install org.deepin.driver.display.nvidia.570-124-04` 安装。其中 `570-124-04` 是驱动版本号,需要与宿主机安装的驱动版本匹配,通过宿主机 `/sys/module/nvidia/version` 文件查看宿主机驱动的版本。未安装扩展时,linyaps 会在运行时尝试从宿主机自动链接 NVIDIA 驱动文件
2424
- 格兰菲显卡驱动,通过 `sudo ll-cli install com.glenfly.driver.display.arise` 安装。
2525
- 英特尔视频编解码驱动(VAAPI),通过 `sudo ll-cli install org.deepin.driver.media.intel` 安装,包含了新/旧 Intel 显卡的支持。

libs/linglong/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,16 @@ pfl_add_library(
2929
src/linglong/cli/dbus_notifier.h
3030
src/linglong/cli/dummy_notifier.cpp
3131
src/linglong/cli/dummy_notifier.h
32+
src/linglong/cli/extension_override.cpp
33+
src/linglong/cli/extension_override.h
3234
src/linglong/cli/interactive_notifier.h
3335
src/linglong/cli/json_printer.cpp
3436
src/linglong/cli/json_printer.h
3537
src/linglong/cli/printer.h
3638
src/linglong/cli/terminal_notifier.cpp
3739
src/linglong/cli/terminal_notifier.h
40+
src/linglong/extension/cdi.cpp
41+
src/linglong/extension/cdi.h
3842
src/linglong/extension/extension.cpp
3943
src/linglong/extension/extension.h
4044
src/linglong/package/architecture.cpp
@@ -94,6 +98,7 @@ pfl_add_library(
9498
src/linglong/runtime/container_builder.h
9599
src/linglong/runtime/container.cpp
96100
src/linglong/runtime/container.h
101+
src/linglong/runtime/host_nvidia_fallback.cpp
97102
src/linglong/runtime/run_context.cpp
98103
src/linglong/runtime/run_context.h
99104
src/linglong/runtime/security_context.cpp

libs/linglong/src/linglong/cli/cli.cpp

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "linglong/api/types/v1/State.hpp"
2626
#include "linglong/api/types/v1/UpgradeListResult.hpp"
2727
#include "linglong/cli/printer.h"
28+
#include "linglong/cli/extension_override.h"
2829
#include "linglong/common/dir.h"
2930
#include "linglong/common/strings.h"
3031
#include "linglong/oci-cfg-generators/container_cfg_builder.h"
@@ -58,7 +59,9 @@
5859
#include <cstdint>
5960
#include <cstdlib>
6061
#include <filesystem>
62+
#include <fstream>
6163
#include <iostream>
64+
#include <map>
6265
#include <optional>
6366
#include <system_error>
6467
#include <thread>
@@ -67,6 +70,7 @@
6770

6871
#include <fcntl.h>
6972
#include <sys/mman.h>
73+
#include <sys/resource.h>
7074
#include <unistd.h>
7175

7276
using namespace linglong::utils::error;
@@ -519,10 +523,21 @@ int Cli::run(const RunOptions &options)
519523
linglong::runtime::ResolveOptions opts;
520524
opts.baseRef = options.base;
521525
opts.runtimeRef = options.runtime;
522-
// 处理多个扩展
523526
if (!options.extensions.empty()) {
524527
opts.extensionRefs = options.extensions;
525528
}
529+
auto configPath = extension_override::getUserConfigPath();
530+
if (configPath) {
531+
auto overrides = extension_override::loadOverrides(*configPath);
532+
if (overrides) {
533+
if (!overrides->empty()) {
534+
runContext.setExtensionOverrides(std::move(*overrides));
535+
}
536+
} else {
537+
qWarning() << "failed to load extension overrides:"
538+
<< overrides.error().message().c_str();
539+
}
540+
}
526541

527542
// 调整日志输出,打印扩展列表(用逗号拼接)
528543
std::string extStr =
@@ -2137,6 +2152,22 @@ utils::error::Result<void> Cli::generateLDCache(runtime::RunContext &runContext,
21372152
{
21382153
LINGLONG_TRACE("generate ld cache");
21392154

2155+
{
2156+
struct rlimit limit {};
2157+
if (::getrlimit(RLIMIT_NOFILE, &limit) == 0) {
2158+
rlim_t target = limit.rlim_max;
2159+
if (target == RLIM_INFINITY) {
2160+
target = 65535;
2161+
}
2162+
if (limit.rlim_cur < target) {
2163+
struct rlimit newLimit { target, limit.rlim_max };
2164+
if (::setrlimit(RLIMIT_NOFILE, &newLimit) != 0) {
2165+
qWarning() << "failed to raise RLIMIT_NOFILE:" << ::strerror(errno);
2166+
}
2167+
}
2168+
}
2169+
}
2170+
21402171
auto appLayerItem = runContext.getCachedAppItem();
21412172
if (!appLayerItem) {
21422173
return LINGLONG_ERR(appLayerItem);
@@ -2211,6 +2242,28 @@ utils::error::Result<void> Cli::generateLDCache(runtime::RunContext &runContext,
22112242
process.args =
22122243
std::vector<std::string>{ "/sbin/ldconfig", "-X", "-C", "/run/linglong/cache/ld.so.cache" };
22132244

2245+
{
2246+
// Ensure ldconfig inside the container has a sufficiently large FD limit.
2247+
// Raising RLIMIT_NOFILE only on the host process may not reliably propagate
2248+
// into the OCI process, depending on the runtime's defaults.
2249+
rlim_t target = 65535;
2250+
struct rlimit limit {};
2251+
if (::getrlimit(RLIMIT_NOFILE, &limit) == 0) {
2252+
if (limit.rlim_max != RLIM_INFINITY) {
2253+
target = limit.rlim_max;
2254+
}
2255+
}
2256+
if (target == RLIM_INFINITY) {
2257+
target = 65535;
2258+
}
2259+
int64_t nofile = static_cast<int64_t>(target);
2260+
process.rlimits = std::vector<ocppi::runtime::config::types::Rlimit>{
2261+
ocppi::runtime::config::types::Rlimit{ .hard = nofile,
2262+
.soft = nofile,
2263+
.type = "RLIMIT_NOFILE" },
2264+
};
2265+
}
2266+
22142267
ocppi::runtime::RunOption opt{};
22152268
auto result = (*container)->run(process, opt);
22162269
if (!result) {
@@ -2327,6 +2380,21 @@ int Cli::inspect(CLI::App *app, const InspectOptions &options)
23272380
return 0;
23282381
}
23292382

2383+
int Cli::extension(CLI::App *app, const ExtensionOptions &options)
2384+
{
2385+
LINGLONG_TRACE("command extension");
2386+
2387+
auto argsParseFunc = [&app](const std::string &name) -> bool {
2388+
return app->get_subcommand(name)->parsed();
2389+
};
2390+
2391+
if (argsParseFunc("import-cdi")) {
2392+
return importCdi(options);
2393+
}
2394+
2395+
return 0;
2396+
}
2397+
23302398
int Cli::getLayerDir(const InspectOptions &options)
23312399
{
23322400
LINGLONG_TRACE("Get Layer dir");
@@ -2388,6 +2456,35 @@ int Cli::getBundleDir(const InspectOptions &options)
23882456
return 0;
23892457
}
23902458

2459+
int Cli::importCdi(const ExtensionOptions &options)
2460+
{
2461+
LINGLONG_TRACE("import CDI config");
2462+
2463+
std::filesystem::path configPath;
2464+
if (options.configPath) {
2465+
configPath = *options.configPath;
2466+
} else {
2467+
auto userConfigPath = extension_override::getUserConfigPath();
2468+
if (!userConfigPath) {
2469+
this->printer.printErr(LINGLONG_ERRV("failed to resolve user config path"));
2470+
return -1;
2471+
}
2472+
configPath = *userConfigPath;
2473+
}
2474+
2475+
auto res = extension_override::importCdiOverrides(configPath,
2476+
options.cdiPath,
2477+
options.name,
2478+
!options.applyWhenInstalled);
2479+
if (!res) {
2480+
this->printer.printErr(res.error());
2481+
return -1;
2482+
}
2483+
2484+
this->printer.printMessage("CDI config imported into " + configPath.string());
2485+
return 0;
2486+
}
2487+
23912488
utils::error::Result<void> Cli::initInteraction()
23922489
{
23932490
LINGLONG_TRACE("initInteraction");

libs/linglong/src/linglong/cli/cli.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,14 @@ struct InspectOptions
134134
std::string dirType{ "layer" };
135135
};
136136

137+
struct ExtensionOptions
138+
{
139+
std::optional<std::string> configPath;
140+
std::optional<std::string> cdiPath;
141+
std::string name{ "org.deepin.driver.display.nvidia" };
142+
bool applyWhenInstalled{ false };
143+
};
144+
137145
enum class TaskType : int {
138146
None,
139147
Install,
@@ -186,6 +194,7 @@ class Cli : public QObject
186194
int content(const ContentOptions &options);
187195
int prune();
188196
int inspect(CLI::App *subcommand, const InspectOptions &options);
197+
int extension(CLI::App *subcommand, const ExtensionOptions &options);
189198

190199
void cancelCurrentTask();
191200

@@ -225,6 +234,7 @@ class Cli : public QObject
225234
std::vector<std::string> getRunningAppContainers(const std::string &appid);
226235
int getLayerDir(const InspectOptions &options);
227236
int getBundleDir(const InspectOptions &options);
237+
int importCdi(const ExtensionOptions &options);
228238
utils::error::Result<void> initInteraction();
229239
void detectDrivers();
230240

0 commit comments

Comments
 (0)