-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Add CDI for WSLC GPU #40583
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Add CDI for WSLC GPU #40583
Changes from all commits
ba81b75
50f9ce0
c55426c
0055811
de88707
f16fc61
da31dd2
b95f237
2ae93aa
f805f05
f2b4090
07445a2
3a514c8
9a29435
fe2770e
66740bb
73a6b39
41b6a41
dcfd971
00522e1
e2d97fd
ba3bc80
ae110fb
e93a2ec
ae3c9d3
5ac6de0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -32,6 +32,10 @@ Module Name: | |
| #include <pty.h> | ||
| #include "mountutilcpp.h" | ||
| #include <filesystem> | ||
| #include <iostream> | ||
| #include "JsonUtils.h" | ||
| #include "cdi_schema.h" | ||
| #include "lxfsshares.h" | ||
|
|
||
| extern int InitializeLogging(bool SetStderr, wil::LogFunction* ExceptionCallback) noexcept; | ||
|
|
||
|
|
@@ -63,19 +67,105 @@ struct WSLCState | |
|
|
||
| static WSLCState g_state; | ||
|
|
||
| int CreateCaptureCrashSymlink() | ||
| void WriteWslcCdiSpec() | ||
| try | ||
| { | ||
| THROW_LAST_ERROR_IF(symlink("/init", "/" LX_INIT_WSL_CAPTURE_CRASH) < 0); | ||
| wsl::shared::cdi::DeviceNode dxg{}; | ||
| dxg.path = "/dev/dxg"; | ||
| dxg.permissions = "rwm"; | ||
|
|
||
| wsl::shared::cdi::Mount libs{}; | ||
| libs.hostPath = LXSS_LIB_PATH; | ||
| libs.containerPath = LXSS_LIB_PATH; | ||
| libs.options = {"ro", "rbind"}; | ||
|
|
||
| wsl::shared::cdi::Mount drivers{}; | ||
| drivers.hostPath = LXSS_GPU_DRIVERS_PATH; | ||
| drivers.containerPath = LXSS_GPU_DRIVERS_PATH; | ||
| drivers.options = {"ro", "rbind"}; | ||
|
|
||
| wsl::shared::cdi::Hook hook{}; | ||
| hook.hookName = "createContainer"; | ||
| hook.path = "/" LX_INIT_WSLC_GPU_HOOK; | ||
| hook.args = {LX_INIT_WSLC_GPU_HOOK}; | ||
|
|
||
| wsl::shared::cdi::Device gpu{}; | ||
| gpu.name = "gpu"; | ||
| gpu.containerEdits.deviceNodes.push_back(std::move(dxg)); | ||
| gpu.containerEdits.mounts.push_back(std::move(libs)); | ||
| gpu.containerEdits.mounts.push_back(std::move(drivers)); | ||
| gpu.containerEdits.hooks.push_back(std::move(hook)); | ||
|
|
||
| wsl::shared::cdi::Spec spec{}; | ||
| spec.cdiVersion = "0.6.0"; | ||
| spec.kind = LX_WSLC_CDI_KIND; | ||
| spec.devices.push_back(std::move(gpu)); | ||
|
|
||
| THROW_LAST_ERROR_IF(UtilMkdirPath("/etc/cdi", 0755) < 0); | ||
| THROW_LAST_ERROR_IF( | ||
| WriteToFile("/etc/cdi/microsoft.com-wslc.json", nlohmann::json(spec).dump().c_str(), O_WRONLY | O_CLOEXEC | O_CREAT | O_TRUNC) < 0); | ||
| } | ||
| CATCH_LOG() | ||
|
|
||
| void WriteDockerDaemonConfig() | ||
| try | ||
| { | ||
| constexpr auto c_daemonConfigPath = "/etc/docker/daemon.json"; | ||
|
|
||
| THROW_ERRNO_IF(EEXIST, std::filesystem::exists(c_daemonConfigPath)); | ||
|
|
||
| nlohmann::json config = nlohmann::json::object(); | ||
| config["features"]["cdi"] = true; | ||
|
|
||
| THROW_LAST_ERROR_IF(UtilMkdirPath("/etc/docker", 0755) < 0); | ||
| THROW_LAST_ERROR_IF(WriteToFile(c_daemonConfigPath, config.dump().c_str(), O_WRONLY | O_CLOEXEC | O_CREAT | O_TRUNC) < 0); | ||
|
kvega005 marked this conversation as resolved.
|
||
| } | ||
|
kvega005 marked this conversation as resolved.
|
||
| CATCH_LOG() | ||
|
|
||
| int WslcGpuHookEntry() | ||
| try | ||
| { | ||
|
kvega005 marked this conversation as resolved.
|
||
| // OCI runtime hooks receive the container state as JSON on stdin. | ||
| const std::string stateJson{std::istreambuf_iterator<char>(std::cin), {}}; | ||
| THROW_ERRNO_IF(EINVAL, stateJson.empty()); | ||
|
|
||
| const auto state = nlohmann::json::parse(stateJson); | ||
| const std::filesystem::path bundle = state.at("bundle").get<std::string>(); | ||
| THROW_ERRNO_IF(EINVAL, !bundle.is_absolute()); | ||
|
|
||
| // Read the OCI spec's root.path from <bundle>/config.json. This is either an absolute path to | ||
| // the overlay-merged rootfs or a path relative to the bundle directory. | ||
| const auto spec = nlohmann::json::parse(UtilReadFileContent((bundle / "config.json").native())); | ||
| std::filesystem::path rootfsPath = spec.at("root").at("path").get<std::string>(); | ||
| if (rootfsPath.is_relative()) | ||
| { | ||
| rootfsPath = bundle / rootfsPath; | ||
| } | ||
|
|
||
| rootfsPath = std::filesystem::canonical(rootfsPath); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is fine, but is there not a canonicalize that modifies the existing path?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think canonicalization is only provided as non-member functions that return a new path. There is no |
||
| THROW_ERRNO_IF(EINVAL, rootfsPath == "/"); | ||
| THROW_ERRNO_IF(ENOTDIR, !std::filesystem::is_directory(rootfsPath)); | ||
|
|
||
| const auto confDir = rootfsPath / "etc/ld.so.conf.d"; | ||
| const auto confPath = confDir / "ld.wsl.conf"; | ||
|
|
||
| THROW_LAST_ERROR_IF(UtilMkdirPath(confDir.c_str(), 0755) < 0); | ||
| THROW_LAST_ERROR_IF(WriteToFile(confPath.c_str(), LXSS_LIB_PATH "\n", O_WRONLY | O_CLOEXEC | O_CREAT | O_TRUNC | O_NOFOLLOW) < 0); | ||
|
|
||
|
kvega005 marked this conversation as resolved.
|
||
| // chroots into the rootfs and uses the container's own /etc/ld.so.conf chain, | ||
| // writing /etc/ld.so.cache inside the container. | ||
| const char* const ldArgv[] = {LDCONFIG_COMMAND, "-r", rootfsPath.c_str(), nullptr}; | ||
| THROW_LAST_ERROR_IF(UtilCreateProcessAndWait(ldArgv[0], ldArgv) < 0); | ||
|
kvega005 marked this conversation as resolved.
|
||
|
|
||
| return 0; | ||
| } | ||
| CATCH_RETURN_ERRNO() | ||
|
|
||
| void WSLCEnableCrashDumpCollection() | ||
| { | ||
| if (CreateCaptureCrashSymlink() < 0) | ||
| if (symlink("/init", "/" LX_INIT_WSL_CAPTURE_CRASH) < 0 && errno != EEXIST) | ||
| { | ||
| LOG_ERROR("symlink(/init, /" LX_INIT_WSL_CAPTURE_CRASH ") failed {}", errno); | ||
| return; | ||
| } | ||
|
|
||
|
|
@@ -619,8 +709,12 @@ void HandleMessageImpl( | |
| { | ||
| THROW_LAST_ERROR_IF(Chroot(target) < 0); | ||
|
|
||
| // Recreate the crash dump symlink inside the new root. | ||
| CreateCaptureCrashSymlink(); | ||
| // Recreate the /init symlinks inside the new root. | ||
| THROW_LAST_ERROR_IF(symlink("/init", "/" LX_INIT_WSL_CAPTURE_CRASH) < 0 && errno != EEXIST); | ||
| THROW_LAST_ERROR_IF(symlink("/init", "/" LX_INIT_WSLC_GPU_HOOK) < 0 && errno != EEXIST); | ||
|
|
||
| WriteWslcCdiSpec(); | ||
| WriteDockerDaemonConfig(); | ||
| } | ||
|
|
||
| response.Result = 0; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -313,6 +313,6 @@ std::string UtilReadFileContent(std::string_view path); | |
|
|
||
| uint16_t UtilWinAfToLinuxAf(uint16_t AddressFamily); | ||
|
|
||
| int WriteToFile(const char* Path, const char* Content, int permissions = 0644); | ||
| int WriteToFile(const char* Path, const char* Content, int OpenFlags = O_WRONLY | O_CLOEXEC | O_CREAT, int Permissions = 0644); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I know why you did things in this order (to match real open argument order), but we should be careful that any callers that were previously passing optional vales for permission are not now passing them as OpenFlags.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I assume you did an audit of everybody calling WriteToFile?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe safest to just remove the default parameters from the def and chase down compiler errors to make sure they are all right.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I checked. There were actually no callers using the third (permissions) parameter at all. We can actually remove that entirely if you want. |
||
|
|
||
|
kvega005 marked this conversation as resolved.
|
||
| int ProcessCreateProcessMessage(wsl::shared::Transaction& Transaction, gsl::span<gsl::byte> Buffer); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| /*++ | ||
|
|
||
| Copyright (c) Microsoft. All rights reserved. | ||
|
|
||
| Module Name: | ||
|
|
||
| cdi_schema.h | ||
|
|
||
| Abstract: | ||
|
|
||
| Schema for Container Device Interface (CDI) specs. | ||
| See https://github.com/cncf-tags/container-device-interface/blob/main/SPEC.md | ||
|
|
||
| --*/ | ||
|
|
||
| #pragma once | ||
|
|
||
| #include "JsonUtils.h" | ||
|
|
||
| namespace wsl::shared::cdi { | ||
|
|
||
| struct DeviceNode | ||
| { | ||
| std::string path; | ||
| std::string permissions; | ||
|
|
||
| NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(DeviceNode, path, permissions); | ||
| }; | ||
|
|
||
| struct Mount | ||
| { | ||
| std::string hostPath; | ||
| std::string containerPath; | ||
| std::vector<std::string> options; | ||
|
|
||
| NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Mount, hostPath, containerPath, options); | ||
| }; | ||
|
|
||
| struct Hook | ||
| { | ||
| std::string hookName; | ||
| std::string path; | ||
| std::vector<std::string> args; | ||
|
|
||
| NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Hook, hookName, path, args); | ||
| }; | ||
|
|
||
| struct ContainerEdits | ||
| { | ||
| std::vector<DeviceNode> deviceNodes; | ||
| std::vector<Mount> mounts; | ||
| std::vector<Hook> hooks; | ||
|
|
||
| NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(ContainerEdits, deviceNodes, mounts, hooks); | ||
| }; | ||
|
|
||
| struct Device | ||
| { | ||
| std::string name; | ||
| ContainerEdits containerEdits; | ||
|
|
||
| NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Device, name, containerEdits); | ||
| }; | ||
|
|
||
| struct Spec | ||
| { | ||
| std::string cdiVersion; | ||
| std::string kind; | ||
| std::vector<Device> devices; | ||
|
|
||
| NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Spec, cdiVersion, kind, devices); | ||
| }; | ||
|
|
||
| } // namespace wsl::shared::cdi |
Uh oh!
There was an error while loading. Please reload this page.