1package main
2
3import (
4 "fmt"
5 "os"
6 "unsafe"
7
8 "golang.org/x/sys/unix"
9)
10
11// udmabuf turns memfd pages into a real dmabuf: a linear buffer any GPU
12// driver imports. fill gets the page-aligned backing store, size bytes long.
13func udmabuf(size int, fill func(data []byte)) (*os.File, error) {
14 dev, err := os.OpenFile("/dev/udmabuf", os.O_RDWR, 0)
15 if err != nil {
16 return nil, fmt.Errorf("udmabuf unavailable: %w", err)
17 }
18 defer dev.Close()
19 size = (size + os.Getpagesize() - 1) / os.Getpagesize() * os.Getpagesize()
20 mem, err := unix.MemfdCreate("testpattern-dmabuf", unix.MFD_ALLOW_SEALING|unix.MFD_CLOEXEC)
21 if err != nil {
22 return nil, err
23 }
24 defer unix.Close(mem)
25 if err := unix.Ftruncate(mem, int64(size)); err != nil {
26 return nil, err
27 }
28 data, err := unix.Mmap(mem, 0, size, unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED)
29 if err != nil {
30 return nil, err
31 }
32 fill(data)
33 if err := unix.Munmap(data); err != nil {
34 return nil, err
35 }
36 if _, err := unix.FcntlInt(uintptr(mem), unix.F_ADD_SEALS, unix.F_SEAL_SHRINK); err != nil {
37 return nil, err
38 }
39 // struct udmabuf_create { u32 memfd; u32 flags; u64 offset; u64 size; }
40 arg := struct {
41 memfd, flags uint32
42 offset, size uint64
43 }{memfd: uint32(mem), flags: 1 /* CLOEXEC */, size: uint64(size)}
44 const udmabufCreate = 0x40187542 // _IOW('u', 0x42, struct udmabuf_create)
45 fd, _, errno := unix.Syscall(unix.SYS_IOCTL, dev.Fd(), udmabufCreate, uintptr(unsafe.Pointer(&arg)))
46 if errno != 0 {
47 return nil, fmt.Errorf("udmabuf create: %w", errno)
48 }
49 return os.NewFile(fd, "dmabuf"), nil
50}