[cgroups2] Handle missing 'kernel' field in 'memory.stat' on linux < 5.18.

The 'kernel' key was introduced to 'memory.stat' in Kernel 5.18 and therefore
isn't present on older kernels. If it is missing, we set `kernel` to be the
sum of the other kernel usage fields provided in 'memory.stat'. This is an
under-accounting since it doesn't include:

 - various kvm allocations (e.g. allocated pages to create vcpus)
 - io_uring
 - tmp_page in pipes during pipe_write()
 - bpf ringbuffers
 - unix sockets

But it's the best measurement we can provide prior to the 'kernel' stat
being added in 5.18 that catches all of these.

As part of this, we add the 'slab' key (one of the kernel memory usage
fields) to the `memory::Stats` structure.

See kernel patch introducing 'kernel':

https://github.com/torvalds/linux/commit/a8c49af3be5f0b4e105ef678bcf14ef102c270be

This closes #576
diff --git a/src/linux/cgroups2.cpp b/src/linux/cgroups2.cpp
index 782c7c4..2057b32 100644
--- a/src/linux/cgroups2.cpp
+++ b/src/linux/cgroups2.cpp
@@ -865,6 +865,7 @@
 {
   Stats stats;
 
+  bool kernel_found = false;
   foreach (const string& line, strings::split(content, "\n")) {
     if (line.empty()) {
       continue;
@@ -893,6 +894,19 @@
     else if (key == "sock")         { stats.sock          = bytes; }
     else if (key == "vmalloc")      { stats.vmalloc       = bytes; }
     else if (key == "file_mapped")  { stats.file_mapped   = bytes; }
+    else if (key == "slab")         { stats.slab          = bytes; }
+
+    kernel_found |= key == "kernel";
+  }
+
+  // See Stats::kernel for an explanation of why this can be missing
+  // and why we fill it in using these sub-metrics:
+  if (!kernel_found) {
+    stats.kernel = stats.kernel_stack
+      + stats.pagetables
+      + stats.sock
+      + stats.vmalloc
+      + stats.slab;
   }
 
   return stats;
diff --git a/src/linux/cgroups2.hpp b/src/linux/cgroups2.hpp
index 247fafd..619c757 100644
--- a/src/linux/cgroups2.hpp
+++ b/src/linux/cgroups2.hpp
@@ -267,6 +267,17 @@
 
   // Amount of total kernel memory, including (kernel_stack, pagetables,
   // percpu, vmalloc, slab) in addition to other kernel memory use cases.
+  //
+  // If this field is missing (linux < 5.18), it's calculated as
+  // the sum of: kernel_stack, pagetables, sock, vmalloc, and slab. This
+  // is an under-accounting since it doesn't include:
+  //   - various kvm allocations (e.g. allocated pages to create vcpus)
+  //   - io_uring
+  //   - tmp_page in pipes during pipe_write()
+  //   - bpf ringbuffers
+  //   - unix sockets
+  // But it's the best measurement we can provide on linux < 5.18.
+  // See: https://github.com/torvalds/linux/commit/a8c49af3be5f0b4e105ef6
   Bytes kernel;
 
   // Amount of memory allocated to kernel stacks.
@@ -283,6 +294,9 @@
 
   // Amount of cached filesystem data mapped with mmap().
   Bytes file_mapped;
+
+  // Amount of memory used for storing in-kernel data structures.
+  Bytes slab;
 };