Skip to content

Commit c4e4f34

Browse files
authored
Merge pull request #4137 from unsuman/driver/krun
Support for `libkrun` using `krunkit`
2 parents 7fad4ec + 0856c25 commit c4e4f34

File tree

7 files changed

+764
-0
lines changed

7 files changed

+764
-0
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// SPDX-FileCopyrightText: Copyright The Lima Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package main
5+
6+
import (
7+
"context"
8+
9+
"github.com/lima-vm/lima/v2/pkg/driver/external/server"
10+
"github.com/lima-vm/lima/v2/pkg/driver/krunkit"
11+
)
12+
13+
// To be used as an external driver for Lima.
14+
func main() {
15+
server.Serve(context.Background(), krunkit.New())
16+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// SPDX-FileCopyrightText: Copyright The Lima Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package krunkit
5+
6+
import "errors"
7+
8+
var errUnimplemented = errors.New("unimplemented by the krunkit driver")
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#!/bin/bash
2+
3+
# SPDX-FileCopyrightText: Copyright The Lima Authors
4+
# SPDX-License-Identifier: Apache-2.0
5+
6+
set -eux -o pipefail
7+
8+
# Install required packages
9+
dnf install -y dnf-plugins-core dnf-plugin-versionlock llvm18-libs
10+
11+
# Install Vulkan and Mesa base packages
12+
dnf install -y \
13+
mesa-vulkan-drivers \
14+
vulkan-loader-devel \
15+
vulkan-headers \
16+
vulkan-tools \
17+
vulkan-loader \
18+
glslc
19+
20+
# Enable COPR repo with patched Mesa for Venus support
21+
dnf copr enable -y slp/mesa-krunkit fedora-40-aarch64
22+
23+
# Downgrade to patched Mesa version from COPR
24+
dnf downgrade -y mesa-vulkan-drivers.aarch64 \
25+
--repo=copr:copr.fedorainfracloud.org:slp:mesa-krunkit
26+
27+
# Lock Mesa version to prevent automatic upgrades
28+
dnf versionlock add mesa-vulkan-drivers
29+
30+
# Clean up
31+
dnf clean all
32+
33+
echo "Krunkit GPU(Venus) setup complete. Verify Vulkan installation by running 'vulkaninfo --summary'."
Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
// SPDX-FileCopyrightText: Copyright The Lima Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package krunkit
5+
6+
import (
7+
"context"
8+
"errors"
9+
"fmt"
10+
"net"
11+
"os"
12+
"os/exec"
13+
"path/filepath"
14+
"strconv"
15+
16+
"github.com/docker/go-units"
17+
"github.com/sirupsen/logrus"
18+
19+
"github.com/lima-vm/lima/v2/pkg/driver/vz"
20+
"github.com/lima-vm/lima/v2/pkg/imgutil/proxyimgutil"
21+
"github.com/lima-vm/lima/v2/pkg/iso9660util"
22+
"github.com/lima-vm/lima/v2/pkg/limatype"
23+
"github.com/lima-vm/lima/v2/pkg/limatype/filenames"
24+
"github.com/lima-vm/lima/v2/pkg/limayaml"
25+
"github.com/lima-vm/lima/v2/pkg/networks"
26+
"github.com/lima-vm/lima/v2/pkg/networks/usernet"
27+
"github.com/lima-vm/lima/v2/pkg/store"
28+
)
29+
30+
const logLevelInfo = "3"
31+
32+
// Cmdline constructs the command line arguments for krunkit based on the instance configuration.
33+
func Cmdline(inst *limatype.Instance) (*exec.Cmd, error) {
34+
memBytes, err := units.RAMInBytes(*inst.Config.Memory)
35+
if err != nil {
36+
return nil, err
37+
}
38+
39+
args := []string{
40+
// Memory in MiB
41+
"--memory", strconv.FormatInt(memBytes/units.MiB, 10),
42+
"--cpus", fmt.Sprintf("%d", *inst.Config.CPUs),
43+
"--device", fmt.Sprintf("virtio-serial,logFilePath=%s", filepath.Join(inst.Dir, filenames.SerialLog)),
44+
"--krun-log-level", logLevelInfo,
45+
"--restful-uri", "none://",
46+
47+
// First virtio-blk device is the boot disk
48+
"--device", fmt.Sprintf("virtio-blk,path=%s,format=raw", filepath.Join(inst.Dir, filenames.DiffDisk)),
49+
"--device", fmt.Sprintf("virtio-blk,path=%s", filepath.Join(inst.Dir, filenames.CIDataISO)),
50+
}
51+
52+
// Add additional disks
53+
if len(inst.Config.AdditionalDisks) > 0 {
54+
ctx := context.Background()
55+
diskUtil := proxyimgutil.NewDiskUtil(ctx)
56+
for _, d := range inst.Config.AdditionalDisks {
57+
disk, derr := store.InspectDisk(d.Name)
58+
if derr != nil {
59+
return nil, fmt.Errorf("failed to load disk %q: %w", d.Name, derr)
60+
}
61+
if disk.Instance != "" {
62+
return nil, fmt.Errorf("failed to run attach disk %q, in use by instance %q", disk.Name, disk.Instance)
63+
}
64+
if lerr := disk.Lock(inst.Dir); lerr != nil {
65+
return nil, fmt.Errorf("failed to lock disk %q: %w", d.Name, lerr)
66+
}
67+
extraDiskPath := filepath.Join(disk.Dir, filenames.DataDisk)
68+
logrus.Infof("Mounting disk %q on %q", disk.Name, disk.MountPoint)
69+
if cerr := diskUtil.ConvertToRaw(ctx, extraDiskPath, extraDiskPath, nil, true); cerr != nil {
70+
return nil, fmt.Errorf("failed to convert extra disk %q to raw: %w", extraDiskPath, cerr)
71+
}
72+
args = append(args, "--device", fmt.Sprintf("virtio-blk,path=%s,format=raw", extraDiskPath))
73+
}
74+
}
75+
76+
// Network commands
77+
networkArgs, err := buildNetworkArgs(inst)
78+
if err != nil {
79+
return nil, fmt.Errorf("failed to build network arguments: %w", err)
80+
}
81+
82+
// File sharing commands
83+
if *inst.Config.MountType == limatype.VIRTIOFS {
84+
for i, mount := range inst.Config.Mounts {
85+
if _, err := os.Stat(mount.Location); errors.Is(err, os.ErrNotExist) {
86+
if err := os.MkdirAll(mount.Location, 0o750); err != nil {
87+
return nil, err
88+
}
89+
}
90+
tag := fmt.Sprintf("mount%d", i)
91+
mountArg := fmt.Sprintf("virtio-fs,sharedDir=%s,mountTag=%s", mount.Location, tag)
92+
args = append(args, "--device", mountArg)
93+
}
94+
}
95+
96+
args = append(args, networkArgs...)
97+
cmd := exec.CommandContext(context.Background(), vmType, args...)
98+
99+
return cmd, nil
100+
}
101+
102+
func buildNetworkArgs(inst *limatype.Instance) ([]string, error) {
103+
var args []string
104+
105+
// Configure default usernetwork with limayaml.MACAddress(inst.Dir) for eth0 interface
106+
firstUsernetIndex := limayaml.FirstUsernetIndex(inst.Config)
107+
if firstUsernetIndex == -1 {
108+
// slirp network using gvisor netstack
109+
krunkitSock, err := usernet.SockWithDirectory(inst.Dir, "", usernet.FDSock)
110+
if err != nil {
111+
return nil, err
112+
}
113+
client, err := vz.PassFDToUnix(krunkitSock)
114+
if err != nil {
115+
return nil, err
116+
}
117+
118+
args = append(args, "--device", fmt.Sprintf("virtio-net,type=unixgram,fd=%d,mac=%s", client.Fd(), limayaml.MACAddress(inst.Dir)))
119+
}
120+
121+
for _, nw := range inst.Networks {
122+
var sock string
123+
var mac string
124+
if nw.Lima != "" {
125+
nwCfg, err := networks.LoadConfig()
126+
if err != nil {
127+
return nil, err
128+
}
129+
switch nw.Lima {
130+
case networks.ModeUserV2:
131+
sock, err = usernet.Sock(nw.Lima, usernet.QEMUSock)
132+
if err != nil {
133+
return nil, err
134+
}
135+
mac = limayaml.MACAddress(inst.Dir)
136+
case networks.ModeShared, networks.ModeBridged:
137+
socketVMNetInstalled, err := nwCfg.IsDaemonInstalled(networks.SocketVMNet)
138+
if err != nil {
139+
return nil, err
140+
}
141+
if !socketVMNetInstalled {
142+
return nil, errors.New("socket_vmnet is not installed")
143+
}
144+
sock, err = networks.Sock(nw.Lima)
145+
if err != nil {
146+
return nil, err
147+
}
148+
mac = nw.MACAddress
149+
default:
150+
return nil, fmt.Errorf("invalid network spec %+v", nw)
151+
}
152+
} else if nw.Socket != "" {
153+
sock = nw.Socket
154+
mac = nw.MACAddress
155+
} else {
156+
return nil, fmt.Errorf("invalid network spec %+v", nw)
157+
}
158+
159+
device := fmt.Sprintf("virtio-net,type=unixstream,path=%s,mac=%s", sock, mac)
160+
args = append(args, "--device", device)
161+
}
162+
163+
if len(args) == 0 {
164+
return args, errors.New("no socket_vmnet networks defined")
165+
}
166+
167+
return args, nil
168+
}
169+
170+
func EnsureDisk(ctx context.Context, inst *limatype.Instance) error {
171+
diffDisk := filepath.Join(inst.Dir, filenames.DiffDisk)
172+
if _, err := os.Stat(diffDisk); err == nil || !errors.Is(err, os.ErrNotExist) {
173+
// disk is already ensured
174+
return err
175+
}
176+
177+
diskUtil := proxyimgutil.NewDiskUtil(ctx)
178+
179+
baseDisk := filepath.Join(inst.Dir, filenames.BaseDisk)
180+
181+
diskSize, _ := units.RAMInBytes(*inst.Config.Disk)
182+
if diskSize == 0 {
183+
return nil
184+
}
185+
isBaseDiskISO, err := iso9660util.IsISO9660(baseDisk)
186+
if err != nil {
187+
return err
188+
}
189+
if isBaseDiskISO {
190+
// Create an empty data volume (sparse)
191+
diffDiskF, err := os.Create(diffDisk)
192+
if err != nil {
193+
return err
194+
}
195+
196+
err = diskUtil.MakeSparse(ctx, diffDiskF, 0)
197+
if err != nil {
198+
diffDiskF.Close()
199+
return fmt.Errorf("failed to create sparse diff disk %q: %w", diffDisk, err)
200+
}
201+
return diffDiskF.Close()
202+
}
203+
204+
// Krunkit also supports qcow2 disks but raw is faster to create and use.
205+
if err = diskUtil.ConvertToRaw(ctx, baseDisk, diffDisk, &diskSize, false); err != nil {
206+
return fmt.Errorf("failed to convert %q to a raw disk %q: %w", baseDisk, diffDisk, err)
207+
}
208+
return err
209+
}
210+
211+
func startUsernet(ctx context.Context, inst *limatype.Instance) (*usernet.Client, context.CancelFunc, error) {
212+
if firstUsernetIndex := limayaml.FirstUsernetIndex(inst.Config); firstUsernetIndex != -1 {
213+
return usernet.NewClientByName(inst.Config.Networks[firstUsernetIndex].Lima), nil, nil
214+
}
215+
// Start a in-process gvisor-tap-vsock
216+
endpointSock, err := usernet.SockWithDirectory(inst.Dir, "", usernet.EndpointSock)
217+
if err != nil {
218+
return nil, nil, err
219+
}
220+
krunkitSock, err := usernet.SockWithDirectory(inst.Dir, "", usernet.FDSock)
221+
if err != nil {
222+
return nil, nil, err
223+
}
224+
os.RemoveAll(endpointSock)
225+
os.RemoveAll(krunkitSock)
226+
ctx, cancel := context.WithCancel(ctx)
227+
err = usernet.StartGVisorNetstack(ctx, &usernet.GVisorNetstackOpts{
228+
MTU: 1500,
229+
Endpoint: endpointSock,
230+
FdSocket: krunkitSock,
231+
Async: true,
232+
DefaultLeases: map[string]string{
233+
networks.SlirpIPAddress: limayaml.MACAddress(inst.Dir),
234+
},
235+
Subnet: networks.SlirpNetwork,
236+
})
237+
if err != nil {
238+
defer cancel()
239+
return nil, nil, err
240+
}
241+
subnetIP, _, err := net.ParseCIDR(networks.SlirpNetwork)
242+
return usernet.NewClient(endpointSock, subnetIP), cancel, err
243+
}

0 commit comments

Comments
 (0)