Subject: [bug#42478] [PATCH] services: Add zram-device-service.

From: Efraim Flashner <[email-masked]>
Date: Wed, 22 Jul 2020 21:10:25 +0300

* gnu/services/linux.scm (<zram-device-configuration>): New record.
(zram-device-service-type): New variable.
* doc/guix.texi (Linux Services): Document it.
* tests/services/linux.scm (zram-device-configuration->udev-string): New
test.
---
 doc/guix.texi            | 45 +++++++++++++++++++++++++
 gnu/services/linux.scm   | 73 +++++++++++++++++++++++++++++++++++++++-
 tests/services/linux.scm | 29 ++++++++++++++++
 3 files changed, 146 insertions(+), 1 deletion(-)

diff --git a/doc/guix.texi b/doc/guix.texi
index 8696a9b554..f656c31fab 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -27127,6 +27127,51 @@ parameters, can be done as follow:
 @end lisp
 @end deffn
 
+@cindex zram
+@cindex compressed swap
+@cindex Compressed RAM-based block devices
+@subsubheading Zram Device Service
+
+The Zram device service provides a compressed swap device in system
+memory.  The Linux Kernel documentation has more information about
+@uref{https://www.kernel.org/doc/html/latest/admin-guide/blockdev/zram.html,zram}
+devices.
+
+@deffn {Scheme Variable} zram-device-service-type
+This service creates the zram block device, formats it as swap and
+enables it as a swap device.  The service's value is a
+@code{zram-device-configuration} record.
+
+@deftp {Data Type} zram-device-configuration
+This is the data type representing the configuration for the zram-device
+service.
+
+@table @asis
+@item @code{disksize} (default @var{"0"})
+This is the amount of space you wish to provide for the zram device.  It
+accepts a string and can be a number of bytes or use a suffix, eg.:
+@var{2G}.
+@item @code{comp_algorithm} (default @var{"lzo"})
+This is the compression algorithm you wish to use.  It is difficult to
+list all the possible compression options, but common ones supported by
+Guix's Linux Libre Kernel include @var{lzo}, @var{lz4} and @var{zstd}.
+@item @code{mem_limit} (default @var{"0"})
+This is the maximum amount of memory which the zram device can use.
+Setting it to '0' disables the limit.  While it is generally expected
+that compression will be 2:1, it is possible that uncompressable data
+can be written to swap and this is a method to limit how much memory can
+be used.  It accepts a string and can be a number of bytes or use a
+suffix, eg.: @var{2G}.
+@item @code{priority} (default @var{"-1"})
+This is the priority of the swap device created from the zram device.
+@code{swapon} accepts values between -1 and 32767, with higher values
+indicating higher priority.  Higher priority swap will generally be used
+first.
+@end table
+
+@end deftp
+@end deffn
+
 @node Hurd Services
 @subsection Hurd Services
 
diff --git a/gnu/services/linux.scm b/gnu/services/linux.scm
index 12934c2084..7073a06cd7 100644
--- a/gnu/services/linux.scm
+++ b/gnu/services/linux.scm
@@ -1,6 +1,7 @@
 ;;; GNU Guix --- Functional package management for GNU
 ;;; Copyright © 2020 Maxim Cournoyer <[email-masked]>
 ;;; Copyright © 2020 Brice Waegeneire <[email-masked]>
+;;; Copyright © 2020 Efraim Flashner <[email-masked]>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -22,6 +23,7 @@
   #:use-module (guix records)
   #:use-module (guix modules)
   #:use-module (gnu services)
+  #:use-module (gnu services base)
   #:use-module (gnu services shepherd)
   #:use-module (gnu packages linux)
   #:use-module (srfi srfi-1)
@@ -42,7 +44,15 @@
             earlyoom-configuration-send-notification-command
             earlyoom-service-type
 
-            kernel-module-loader-service-type))
+            kernel-module-loader-service-type
+
+            zram-device-configuration
+            zram-device-configuration?
+            zram-device-configuration-disksize
+            zram-device-configuration-comp_algorithm
+            zram-device-configuration-mem_limit
+            zram-device-configuration-priority
+            zram-device-service-type))
 
 
 ;;;
@@ -177,3 +187,64 @@ representation."
    (compose concatenate)
    (extend append)
    (default-value '())))
+
+
+;;;
+;;; Kernel module loader.
+;;;
+
+(define-record-type* <zram-device-configuration>
+  zram-device-configuration make-zram-device-configuration
+  zram-device-configuration?
+  (disksize         zram-device-configration-disksize
+                    (default "0"))      ; string
+  (comp_algorithm   zram-device-configuration-comp_algorithm
+                    (default "lzo"))    ; string
+  (mem_limit        zram-device-configuration-mem_limit
+                    (default "0"))      ; string
+  (priority         zram-device-configuration-priority
+                    (default "-1")))    ; string
+
+(define (zram-device-configuration->udev-string config)
+  "Translate a <zram-device-configuration> into a string which can be
+placed in a udev rules file."
+  (match config
+    (($ <zram-device-configuration> disksize comp_algorithm mem_limit priority)
+     (string-append
+       "KERNEL==\"zram0\", "
+       "ATTR{comp_algorithm}=\"" comp_algorithm "\" "
+       (if (not (equal? "0" disksize))
+         (string-append "ATTR{disksize}=\"" disksize "\" ")
+         "")
+       (if (not (equal? "0" mem_limit))
+         (string-append "ATTR{mem_limit}=\"" mem_limit "\" ")
+         "")
+       "RUN+=\"/run/current-system/profile/sbin/mkswap /dev/zram0\" "
+       "RUN+=\"/run/current-system/profile/sbin/swapon "
+       (if (not (equal? "-1" priority))
+         (string-append "--priority " priority " ")
+         "")
+       "/dev/zram0\"\n"))))
+
+(define %zram-device-config
+  `("modprobe.d/zram.conf"
+    ,(plain-file "zram.conf"
+                 "options zram num_devices=1")))
+
+(define (zram-device-udev-rule config)
+  (file->udev-rule "99-zram.rules"
+                   (plain-file "99-zram.rules"
+                               (zram-device-configuration->udev-string config))))
+
+(define zram-device-service-type
+  (service-type
+    (name 'zram)
+    (default-value (zram-device-configuration))
+    (extensions
+      (list (service-extension kernel-module-loader-service-type
+                               (const (list "zram")))
+            (service-extension etc-service-type
+                               (const (list %zram-device-config)))
+            (service-extension udev-service-type
+                               (compose list zram-device-udev-rule))))
+    (description "Creates a zram swap device.")))
diff --git a/tests/services/linux.scm b/tests/services/linux.scm
index 8ad119c49f..828aa86bd6 100644
--- a/tests/services/linux.scm
+++ b/tests/services/linux.scm
@@ -1,5 +1,6 @@
 ;;; GNU Guix --- Functional package management for GNU
 ;;; Copyright © 2020 Maxim Cournoyer <[email-masked]>
+;;; Copyright © 2020 Efraim Flashner <[email-masked]>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -54,4 +55,32 @@
         "-N" "python \"/some/path/notify-all-users.py\"")
   (earlyoom-configuration->command-line-args %earlyoom-configuration-sample))
 
+
+;;;
+;;; Zram swap device.
+;;;
+
+(define zram-device-configuration->udev-string
+  (@@ (gnu services linux) zram-device-configuration->udev-string))
+
+(define %zram-swap-device-test-1
+  (zram-device-configuration
+    (disksize "2G")
+    (comp_algorithm "zstd")
+    (mem_limit "1G")
+    (priority "42")))
+
+(test-equal "zram-device-configuration->udev-string"
+  "KERNEL==\"zram0\", ATTR{comp_algorithm}=\"zstd\" ATTR{disksize}=\"2G\" ATTR{mem_limit}=\"1G\" RUN+=\"/run/current-system/profile/sbin/mkswap /dev/zram0\" RUN+=\"/run/current-system/profile/sbin/swapon --priority 42 /dev/zram0\"\n"
+  (zram-device-configuration->udev-string %zram-swap-device-test-1))
+
+(define %zram-swap-device-test-2
+  (zram-device-configuration
+    (disksize "1G")
+    (comp_algorithm "lz4")))
+
+(test-equal "zram-device-configuration->udev-string"
+  "KERNEL==\"zram0\", ATTR{comp_algorithm}=\"lz4\" ATTR{disksize}=\"1G\" RUN+=\"/run/current-system/profile/sbin/mkswap /dev/zram0\" RUN+=\"/run/current-system/profile/sbin/swapon /dev/zram0\"\n"
+  (zram-device-configuration->udev-string %zram-swap-device-test-2))
+
 (test-end "linux-services")
-- 
2.27.0