Skip to content

Commit

Permalink
Merge pull request #6 from maximumadmin/detect-vm
Browse files Browse the repository at this point in the history
Optionally skip initialization if running on a VM
  • Loading branch information
maximumadmin committed Apr 3, 2021
2 parents 37ab7c2 + f72a810 commit c99577f
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 10 deletions.
4 changes: 3 additions & 1 deletion README.md
Expand Up @@ -53,7 +53,7 @@ See also https://fedoraproject.org/wiki/Changes/SwapOnZRAM#Benefit_to_Fedora

* zramd start --help
```
Usage: zramd start [--algorithm ALGORITHM] [--fraction FRACTION] [--max-size MAX_SIZE] [--num-devices NUM_DEVICES] [--priority PRIORITY]
Usage: zramd start [--algorithm ALGORITHM] [--fraction FRACTION] [--max-size MAX_SIZE] [--num-devices NUM_DEVICES] [--priority PRIORITY] [--skip-vm]
Options:
--algorithm ALGORITHM, -a ALGORITHM
Expand All @@ -66,6 +66,7 @@ See also https://fedoraproject.org/wiki/Changes/SwapOnZRAM#Benefit_to_Fedora
maximum number of zram devices to create [default: 1]
--priority PRIORITY, -p PRIORITY
swap priority [default: 100]
--skip-vm, -s skip initialization if running on a VM [default: false]
--help, -h display this help and exit
```

Expand Down Expand Up @@ -106,6 +107,7 @@ See also https://fedoraproject.org/wiki/Changes/SwapOnZRAM#Benefit_to_Fedora
* **Avoid** using other zram-related packages along this one, `zramd` loads and unloads the zram kernel module assuming that the system is not using zram for other stuff e.g. tmpfs.
* Do **not** use zswap with zram, it would unnecessarily cause data to be [compressed and decompressed back and forth](https://www.phoronix.com/forums/forum/software/distributions/1231542-fedora-34-looking-to-tweak-default-zram-configuration/page5#post1232327).
* When dealing with virtual machines, zram should be used on the **host** OS so guest memory can be compressed transparently, see also comments on original zram [implementation](https://code.google.com/archive/p/compcache/).
* If you boot the same system on a real computer as well as on a virtual machine, you can use the `--skip-vm` parameter to avoid initialization when running inside a virtual machine.
* For best results install `systemd-oomd` or `earlyoom` (they may not be available on all distributions).
* You can use `swapon -show` or `zramctl` to see all swap devices currently in use, this is useful if you want to confirm that all of the zram devices were setup correctly.
* To quickly fill the memory, you can use `tail /dev/zero` but keep in mind that your system may become unresponsive if you do not have an application like `earlyoom` to kill `tail` just before it reaches the memory limit.
1 change: 1 addition & 0 deletions cspell.json
Expand Up @@ -5,6 +5,7 @@
"itertools",
"lsmod",
"mkswap",
"virt",
"zram",
"zramd",
"zstd"
Expand Down
3 changes: 3 additions & 0 deletions extra/zramd.default
Expand Up @@ -12,3 +12,6 @@

# Swap priority
# PRIORITY=100

# Skip initialization if running inside a virtual machine
# SKIP_VM=false
45 changes: 45 additions & 0 deletions src/system/system.go
@@ -0,0 +1,45 @@
package system

import (
"errors"
"os"
"os/exec"
"regexp"
"strings"
)

// IsRoot will check if current process was started by the init system (e.g.
// systemd) from which we expect to handle this process' capabilities, otherwise
// check if the current process is running as root.
func IsRoot() bool {
return os.Getppid() == 1 || os.Geteuid() == 0
}

func cpuInfo() *[]byte {
data, err := os.ReadFile("/proc/cpuinfo")
if err != nil {
panic(err)
}
return &data
}

// IsVM detects if we are currently running inside a VM, see also
// https://man.archlinux.org/man/systemd-detect-virt.1.en.
func IsVM() bool {
// Try to run systemd-detect-virt which is more accurate but is not present on
// all systems.
cmd := exec.Command("systemd-detect-virt", "--vm")
out, err := cmd.Output()
if err == nil {
return strings.TrimSpace(string(out)) != "none"
}
// If error happened because systemd-detect-virt is not available on the
// system, try to use cpuinfo (less accurate but available everywhere).
if errors.Is(err, exec.ErrNotFound) {
info := cpuInfo()
pattern := "(?m)^flags\\s*\\:.*\\s+hypervisor(?:\\s+.*)?$"
match, _ := regexp.Match(pattern, *info)
return match
}
panic(err)
}
21 changes: 12 additions & 9 deletions src/zramd.go
Expand Up @@ -7,6 +7,7 @@ import (
"sync"
"zramd/src/kernelversion"
"zramd/src/memory"
"zramd/src/system"
"zramd/src/zram"

"github.com/alexflint/go-arg"
Expand All @@ -26,6 +27,7 @@ type startCmd struct {
MaxSizeMB int `arg:"-m,--max-size,env:MAX_SIZE" default:"8192" placeholder:"MAX_SIZE" help:"maximum total MB of swap to allocate"`
NumDevices int `arg:"-n,--num-devices,env:NUM_DEVICES" default:"1" placeholder:"NUM_DEVICES" help:"maximum number of zram devices to create"`
SwapPriority int `arg:"-p,--priority,env:PRIORITY" default:"100" placeholder:"PRIORITY" help:"swap priority"`
SkipVM bool `arg:"-s,--skip-vm,env:SKIP_VM" default:"false" help:"skip initialization if running on a VM"`
}

type stopCmd struct {
Expand Down Expand Up @@ -139,13 +141,6 @@ func deinitializeZram() int {
return ret
}

// canRun will check if current process was started by the init system (e.g.
// systemd) from which we expect to handle this process' capabilities, otherwise
// check if the current process is running as root.
func canRun() bool {
return os.Getppid() == 1 || os.Geteuid() == 0
}

func run() int {
if len(os.Args) > 1 && os.Args[1] == "--version" {
fmt.Printf("zramd %s %s %s\n", Version, CommitDate, runtime.GOARCH)
Expand Down Expand Up @@ -199,11 +194,19 @@ func run() int {
if args.Start.SwapPriority < -1 || args.Start.SwapPriority > 32767 {
parser.Fail("--priority must have a value between -1 and 32767")
}
// Avoid initializing zram if running on a virtual machine, we are not
// relying on systemd's "ConditionVirtualization=!vm" because it requires
// systemd, also we want this to be an opt-in setting unlike
// ConditionVirtualization which would behave the other way around.
if args.Start.SkipVM && system.IsVM() {
fmt.Println("virtual machine detected, initialization skipped")
return 0
}
if zram.IsLoaded() {
errorf("the zram module is already loaded")
return 1
}
if !canRun() {
if !system.IsRoot() {
errorf("root privileges are required")
return 1
}
Expand All @@ -214,7 +217,7 @@ func run() int {
errorf("the zram module is not loaded")
return 1
}
if !canRun() {
if !system.IsRoot() {
errorf("root privileges are required")
return 1
}
Expand Down

0 comments on commit c99577f

Please sign in to comment.