/* SPDX-License-Identifier: LGPL-2.1+ */
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
-
#include "config.h"
-#ifdef HAVE_FUSE3
-#ifndef FUSE_USE_VERSION
-#define FUSE_USE_VERSION 30
-#endif
-#else
-#ifndef FUSE_USE_VERSION
-#define FUSE_USE_VERSION 26
-#endif
-#endif
-
-#define _FILE_OFFSET_BITS 64
-
-#define __STDC_FORMAT_MACROS
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
-#include <fuse.h>
#include <inttypes.h>
#include <libgen.h>
#include <pthread.h>
#include <sys/sysinfo.h>
#include <sys/vfs.h>
+#include "proc_fuse.h"
+
#include "bindings.h"
#include "cgroup_fuse.h"
#include "cgroups/cgroup.h"
uint64_t total_unevictable;
};
+static off_t get_procfile_size(const char *path)
+{
+ __do_fclose FILE *f = NULL;
+ __do_free char *line = NULL;
+ size_t len = 0;
+ ssize_t sz, answer = 0;
+
+ f = fopen(path, "re");
+ if (!f)
+ return 0;
+
+ while ((sz = getline(&line, &len, f)) != -1)
+ answer += sz;
+
+ return answer;
+}
+
__lxcfs_fuse_ops int proc_getattr(const char *path, struct stat *sb)
{
struct timespec now;
strcmp(path, "/proc/stat") == 0 ||
strcmp(path, "/proc/diskstats") == 0 ||
strcmp(path, "/proc/swaps") == 0 ||
- strcmp(path, "/proc/loadavg") == 0) {
- sb->st_size = 4096;
+ strcmp(path, "/proc/loadavg") == 0 ||
+ strcmp(path, "/proc/slabinfo") == 0) {
+ sb->st_size = get_procfile_size(path);
sb->st_mode = S_IFREG | 00444;
sb->st_nlink = 1;
return 0;
DIR_FILLER(filler, buf, "uptime", NULL, 0) != 0 ||
DIR_FILLER(filler, buf, "diskstats", NULL, 0) != 0 ||
DIR_FILLER(filler, buf, "swaps", NULL, 0) != 0 ||
- DIR_FILLER(filler, buf, "loadavg", NULL, 0) != 0)
+ DIR_FILLER(filler, buf, "loadavg", NULL, 0) != 0 ||
+ DIR_FILLER(filler, buf, "slabinfo", NULL, 0) != 0)
return -EINVAL;
return 0;
}
-static off_t get_procfile_size(const char *path)
-{
- __do_fclose FILE *f = NULL;
- __do_free char *line = NULL;
- size_t len = 0;
- ssize_t sz, answer = 0;
-
- f = fopen(path, "re");
- if (!f)
- return 0;
-
- while ((sz = getline(&line, &len, f)) != -1)
- answer += sz;
-
- return answer;
-}
-
__lxcfs_fuse_ops int proc_open(const char *path, struct fuse_file_info *fi)
{
__do_free struct file_info *info = NULL;
type = LXC_TYPE_PROC_SWAPS;
else if (strcmp(path, "/proc/loadavg") == 0)
type = LXC_TYPE_PROC_LOADAVG;
+ else if (strcmp(path, "/proc/slabinfo") == 0)
+ type = LXC_TYPE_PROC_SLABINFO;
if (type == -1)
return -ENOENT;
return strncmp(line, pref, strlen(pref)) == 0;
}
+static void get_swap_info(const char *cgroup, uint64_t memlimit,
+ uint64_t memusage, uint64_t *swtotal,
+ uint64_t *swusage, uint64_t *memswpriority)
+{
+ __do_free char *memswusage_str = NULL, *memswpriority_str = NULL;
+ size_t memswlimit = 0, memswusage = 0;
+ int ret;
+
+ *swtotal = *swusage = 0;
+ *memswpriority = 1;
+
+ memswlimit = get_min_memlimit(cgroup, true);
+ if (memswlimit > 0) {
+ ret = cgroup_ops->get_memory_swap_current(cgroup_ops, cgroup, &memswusage_str);
+ if (ret < 0 || safe_uint64(memswusage_str, &memswusage, 10) != 0)
+ return;
+
+ if (liblxcfs_memory_is_cgroupv2()) {
+ *swtotal = memswlimit / 1024;
+ *swusage = memswusage / 1024;
+ } else {
+ if (memlimit > memswlimit)
+ *swtotal = 0;
+ else
+ *swtotal = (memswlimit - memlimit) / 1024;
+ if (memusage > memswusage || swtotal == 0)
+ *swusage = 0;
+ else
+ *swusage = (memswusage - memusage) / 1024;
+ }
+
+ ret = cgroup_ops->get_memory_swappiness(cgroup_ops, cgroup, &memswpriority_str);
+ if (ret >= 0)
+ safe_uint64(memswpriority_str, memswpriority, 10);
+ }
+}
+
static int proc_swaps_read(char *buf, size_t size, off_t offset,
struct fuse_file_info *fi)
{
- __do_free char *cgroup = NULL, *memusage_str = NULL, *memswusage_str = NULL;
+ __do_free char *cgroup = NULL, *memusage_str = NULL,
+ *memswusage_str = NULL, *memswpriority_str = NULL;
struct fuse_context *fc = fuse_get_context();
- struct lxcfs_opts *opts = (struct lxcfs_opts *)fuse_get_context()->private_data;
- bool wants_swap = opts && !opts->swap_off && liblxcfs_can_use_swap();
+ bool wants_swap = lxcfs_has_opt(fuse_get_context()->private_data, LXCFS_SWAP_ON);
struct file_info *d = INTTYPE_TO_PTR(fi->fh);
- uint64_t memswlimit = 0, memlimit = 0, memusage = 0, memswusage = 0,
- swtotal = 0, swfree = 0, swusage = 0;
+ uint64_t memlimit = 0, memusage = 0,
+ swtotal = 0, swusage = 0, memswpriority = 1,
+ hostswtotal = 0, hostswfree = 0;
ssize_t total_len = 0;
ssize_t l = 0;
char *cache = d->buf;
int ret;
+ __do_free char *line = NULL;
+ __do_free void *fopen_cache = NULL;
+ __do_fclose FILE *f = NULL;
+ size_t linelen = 0;
if (offset) {
- int left;
+ size_t left;
if (offset > d->size)
return -EINVAL;
if (safe_uint64(memusage_str, &memusage, 10) < 0)
lxcfs_error("Failed to convert memusage %s", memusage_str);
- if (wants_swap) {
- memswlimit = get_min_memlimit(cgroup, true);
- if (memswlimit > 0) {
- ret = cgroup_ops->get_memory_swap_current(cgroup_ops, cgroup, &memswusage_str);
- if (ret >= 0 && safe_uint64(memswusage_str, &memswusage, 10) == 0) {
- if (memlimit > memswlimit)
- swtotal = 0;
- else
- swtotal = (memswlimit - memlimit) / 1024;
- if (memusage > memswusage || swtotal == 0)
- swusage = 0;
- else
- swusage = (memswusage - memusage) / 1024;
- if (swtotal >= swusage)
- swfree = swtotal - swusage;
- }
- }
- }
+ if (wants_swap)
+ get_swap_info(cgroup, memlimit, memusage, &swtotal, &swusage, &memswpriority);
total_len = snprintf(d->buf, d->size, "Filename\t\t\t\tType\t\tSize\tUsed\tPriority\n");
- /* When no mem + swap limit is specified or swapaccount=0*/
- if (!memswlimit) {
- __do_free char *line = NULL;
- __do_free void *fopen_cache = NULL;
- __do_fclose FILE *f = NULL;
- size_t linelen = 0;
+ /* Read host total and free values */
+ f = fopen_cached("/proc/meminfo", "re", &fopen_cache);
+ if (!f)
+ return 0;
+
+ while (getline(&line, &linelen, f) != -1) {
+ if (startswith(line, "SwapTotal:"))
+ sscanf(line, "SwapTotal: %8" PRIu64 " kB", &hostswtotal);
+ else if (startswith(line, "SwapFree:"))
+ sscanf(line, "SwapFree: %8" PRIu64 " kB", &hostswfree);
+ }
- f = fopen_cached("/proc/meminfo", "re", &fopen_cache);
- if (!f)
- return 0;
+ if (wants_swap) {
+ /* The total amount of swap is always reported to be the
+ lesser of the RAM+SWAP limit or the SWAP device size.
+ This is because the kernel can swap as much as it
+ wants and not only up to swtotal. */
+ swtotal = memlimit / 1024 + swtotal;
+ if (hostswtotal < swtotal) {
+ swtotal = hostswtotal;
+ }
- while (getline(&line, &linelen, f) != -1) {
- if (startswith(line, "SwapTotal:"))
- sscanf(line, "SwapTotal: %8" PRIu64 " kB", &swtotal);
- else if (startswith(line, "SwapFree:"))
- sscanf(line, "SwapFree: %8" PRIu64 " kB", &swfree);
+ /* When swappiness is 0, pretend we can't swap. */
+ if (memswpriority == 0) {
+ swtotal = swusage;
}
}
if (swtotal > 0) {
l = snprintf(d->buf + total_len, d->size - total_len,
"none%*svirtual\t\t%" PRIu64 "\t%" PRIu64 "\t0\n",
- 36, " ", swtotal, swfree);
+ 36, " ", swtotal, swusage);
total_len += l;
}
d->cached = 1;
d->size = (int)total_len;
- if (total_len > size)
+ if ((size_t)total_len > size)
total_len = size;
memcpy(buf, d->buf, total_len);
}
}
+struct lxcfs_diskstats {
+ unsigned int major; /* 1 - major number */
+ unsigned int minor; /* 2 - minor mumber */
+ char dev_name[72]; /* 3 - device name */
+ uint64_t read; /* 4 - reads completed successfully */
+ uint64_t read_merged; /* 5 - reads merged */
+ uint64_t read_sectors; /* 6 - sectors read */
+ uint64_t read_ticks; /* 7 - time spent reading (ms) */
+ uint64_t write; /* 8 - writes completed */
+ uint64_t write_merged; /* 9 - writes merged */
+ uint64_t write_sectors; /* 10 - sectors written */
+ uint64_t write_ticks; /* 11 - time spent writing (ms) */
+ uint64_t ios_pgr; /* 12 - I/Os currently in progress */
+ uint64_t total_ticks; /* 13 - time spent doing I/Os (ms) */
+ uint64_t rq_ticks; /* 14 - weighted time spent doing I/Os (ms) */
+ uint64_t discard; /* 15 - discards completed successfully (4.18+) */
+ uint64_t discard_merged; /* 16 - discards merged (4.18+) */
+ uint64_t discard_sectors; /* 17 - sectors discarded (4.18+) */
+ uint64_t discard_ticks; /* 18 - time spent discarding (4.18+) */
+};
+
static int proc_diskstats_read(char *buf, size_t size, off_t offset,
struct fuse_file_info *fi)
{
__do_fclose FILE *f = NULL;
struct fuse_context *fc = fuse_get_context();
struct file_info *d = INTTYPE_TO_PTR(fi->fh);
- uint64_t read = 0, write = 0;
- uint64_t read_merged = 0, write_merged = 0;
- uint64_t read_sectors = 0, write_sectors = 0;
- uint64_t read_ticks = 0, write_ticks = 0;
- uint64_t ios_pgr = 0, tot_ticks = 0, rq_ticks = 0;
- uint64_t rd_svctm = 0, wr_svctm = 0, rd_wait = 0, wr_wait = 0;
+ struct lxcfs_diskstats stats = {};
+ /* helper fields */
+ uint64_t read_service_time, write_service_time, discard_service_time, read_wait_time,
+ write_wait_time, discard_wait_time;
char *cache = d->buf;
size_t cache_size = d->buflen;
size_t linelen = 0, total_len = 0;
- unsigned int major = 0, minor = 0;
int i = 0;
int ret;
- char dev_name[72];
if (offset) {
- int left;
+ size_t left;
if (offset > d->size)
return -EINVAL;
ssize_t l;
char lbuf[256];
- i = sscanf(line, "%u %u %71s", &major, &minor, dev_name);
+ i = sscanf(line, "%u %u %71s", &stats.major, &stats.minor, stats.dev_name);
if (i != 3)
continue;
- get_blkio_io_value(io_serviced_str, major, minor, "Read", &read);
- get_blkio_io_value(io_serviced_str, major, minor, "Write", &write);
- get_blkio_io_value(io_merged_str, major, minor, "Read", &read_merged);
- get_blkio_io_value(io_merged_str, major, minor, "Write", &write_merged);
- get_blkio_io_value(io_service_bytes_str, major, minor, "Read", &read_sectors);
- read_sectors = read_sectors/512;
- get_blkio_io_value(io_service_bytes_str, major, minor, "Write", &write_sectors);
- write_sectors = write_sectors/512;
-
- get_blkio_io_value(io_service_time_str, major, minor, "Read", &rd_svctm);
- rd_svctm = rd_svctm/1000000;
- get_blkio_io_value(io_wait_time_str, major, minor, "Read", &rd_wait);
- rd_wait = rd_wait/1000000;
- read_ticks = rd_svctm + rd_wait;
-
- get_blkio_io_value(io_service_time_str, major, minor, "Write", &wr_svctm);
- wr_svctm = wr_svctm/1000000;
- get_blkio_io_value(io_wait_time_str, major, minor, "Write", &wr_wait);
- wr_wait = wr_wait/1000000;
- write_ticks = wr_svctm + wr_wait;
-
- get_blkio_io_value(io_service_time_str, major, minor, "Total", &tot_ticks);
- tot_ticks = tot_ticks/1000000;
+ get_blkio_io_value(io_serviced_str, stats.major, stats.minor, "Read", &stats.read);
+ get_blkio_io_value(io_serviced_str, stats.major, stats.minor, "Write", &stats.write);
+ get_blkio_io_value(io_serviced_str, stats.major, stats.minor, "Discard", &stats.discard);
+
+ get_blkio_io_value(io_merged_str, stats.major, stats.minor, "Read", &stats.read_merged);
+ get_blkio_io_value(io_merged_str, stats.major, stats.minor, "Write", &stats.write_merged);
+ get_blkio_io_value(io_merged_str, stats.major, stats.minor, "Discard", &stats.discard_merged);
+
+ get_blkio_io_value(io_service_bytes_str, stats.major, stats.minor, "Read", &stats.read_sectors);
+ stats.read_sectors = stats.read_sectors / 512;
+ get_blkio_io_value(io_service_bytes_str, stats.major, stats.minor, "Write", &stats.write_sectors);
+ stats.write_sectors = stats.write_sectors / 512;
+ get_blkio_io_value(io_service_bytes_str, stats.major, stats.minor, "Discard", &stats.discard_sectors);
+ stats.discard_sectors = stats.discard_sectors / 512;
+
+ get_blkio_io_value(io_service_time_str, stats.major, stats.minor, "Read", &read_service_time);
+ read_service_time = read_service_time / 1000000;
+ get_blkio_io_value(io_wait_time_str, stats.major, stats.minor, "Read", &read_wait_time);
+ read_wait_time = read_wait_time / 1000000;
+ stats.read_ticks = read_service_time + read_wait_time;
+
+ get_blkio_io_value(io_service_time_str, stats.major, stats.minor, "Write", &write_service_time);
+ write_service_time = write_service_time / 1000000;
+ get_blkio_io_value(io_wait_time_str, stats.major, stats.minor, "Write", &write_wait_time);
+ write_wait_time = write_wait_time / 1000000;
+ stats.write_ticks = write_service_time + write_wait_time;
+
+ get_blkio_io_value(io_service_time_str, stats.major, stats.minor, "Discard", &discard_service_time);
+ discard_service_time = discard_service_time / 1000000;
+ get_blkio_io_value(io_wait_time_str, stats.major, stats.minor, "Discard", &discard_wait_time);
+ discard_wait_time = discard_wait_time / 1000000;
+ stats.discard_ticks = discard_service_time + discard_wait_time;
+
+ get_blkio_io_value(io_service_time_str, stats.major, stats.minor, "Total", &stats.total_ticks);
+ stats.total_ticks = stats.total_ticks / 1000000;
memset(lbuf, 0, 256);
- if (read || write || read_merged || write_merged || read_sectors || write_sectors || read_ticks || write_ticks)
- snprintf(lbuf, 256, "%u %u %s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu\n",
- major, minor, dev_name, read, read_merged, read_sectors, read_ticks,
- write, write_merged, write_sectors, write_ticks, ios_pgr, tot_ticks, rq_ticks);
+ if (stats.read || stats.write || stats.read_merged || stats.write_merged ||
+ stats.read_sectors || stats.write_sectors || stats.read_ticks ||
+ stats.write_ticks || stats.ios_pgr || stats.total_ticks || stats.rq_ticks ||
+ stats.discard_merged || stats.discard_sectors || stats.discard_ticks)
+ snprintf(lbuf, 256, "%u %u %s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu\n",
+ stats.major,
+ stats.minor,
+ stats.dev_name,
+ stats.read,
+ stats.read_merged,
+ stats.read_sectors,
+ stats.read_ticks,
+ stats.write,
+ stats.write_merged,
+ stats.write_sectors,
+ stats.write_ticks,
+ stats.ios_pgr,
+ stats.total_ticks,
+ stats.rq_ticks,
+ stats.discard_merged,
+ stats.discard_sectors,
+ stats.discard_ticks);
else
continue;
l = snprintf(cache, cache_size, "%s", lbuf);
if (l < 0)
return log_error(0, "Failed to write cache");
- if (l >= cache_size)
+ if ((size_t)l >= cache_size)
return log_error(0, "Write to cache was truncated");
cache += l;
return total_len;
}
-#if RELOADTEST
+#ifdef RELOADTEST
static inline void iwashere(void)
{
mknod("/tmp/lxcfs-iwashere", S_IFREG, 0644);
ssize_t total_len = 0, ret = 0;
double busytime, idletime, reaperage;
-#if RELOADTEST
+#ifdef RELOADTEST
iwashere();
#endif
if (offset) {
- int left;
+ size_t left;
if (offset > d->size)
return -EINVAL;
d->cached = 1;
d->size = total_len;
- if (total_len > size)
+ if ((size_t)total_len > size)
total_len = size;
memcpy(buf, d->buf, total_len);
int cg_cpu_usage_size = 0;
if (offset) {
- int left;
+ size_t left;
if (offset > d->size)
return -EINVAL;
l = snprintf(cache, cache_size, "%s", line);
if (l < 0)
return log_error(0, "Failed to write cache");
- if (l >= cache_size)
+ if ((size_t)l >= cache_size)
return log_error(0, "Write to cache was truncated");
cache += l;
l = snprintf(cache, cache_size, "cpu%d%s", curcpu, c);
if (l < 0)
return log_error(0, "Failed to write cache");
- if (l >= cache_size)
+ if ((size_t)l >= cache_size)
return log_error(0, "Write to cache was truncated");
cache += l;
if (all_used >= cg_used) {
new_idle = idle + (all_used - cg_used);
-
} else {
- lxcfs_error("cpu%d from %s has unexpected cpu time: %" PRIu64 " in /proc/stat, %" PRIu64 " in cpuacct.usage_all; unable to determine idle time",
+ lxcfs_debug("cpu%d from %s has unexpected cpu time: %" PRIu64 " in /proc/stat, %" PRIu64 " in cpuacct.usage_all; unable to determine idle time",
curcpu, cg, all_used, cg_used);
new_idle = idle;
}
cg_cpu_usage[physcpu].system, new_idle);
if (l < 0)
return log_error(0, "Failed to write cache");
- if (l >= cache_size)
+ if ((size_t)l >= cache_size)
return log_error(0, "Write to cache was truncated");
cache += l;
struct fuse_file_info *fi)
{
__do_free char *cgroup = NULL, *line = NULL, *memusage_str = NULL,
- *memswusage_str = NULL;
+ *memswusage_str = NULL, *memswpriority_str = NULL;
__do_free void *fopen_cache = NULL;
__do_fclose FILE *f = NULL;
struct fuse_context *fc = fuse_get_context();
- struct lxcfs_opts *opts = (struct lxcfs_opts *)fuse_get_context()->private_data;
- bool wants_swap = opts && !opts->swap_off && liblxcfs_can_use_swap(), host_swap = false;
+ bool wants_swap = lxcfs_has_opt(fuse_get_context()->private_data, LXCFS_SWAP_ON);
struct file_info *d = INTTYPE_TO_PTR(fi->fh);
- uint64_t memlimit = 0, memusage = 0, memswlimit = 0, memswusage = 0,
- hosttotal = 0, swfree = 0, swusage = 0, swtotal = 0;
+ uint64_t memlimit = 0, memusage = 0,
+ hosttotal = 0, swfree = 0, swusage = 0, swtotal = 0,
+ memswpriority = 1;
struct memory_stat mstat = {};
size_t linelen = 0, total_len = 0;
char *cache = d->buf;
int ret;
if (offset) {
- int left;
+ size_t left;
if (offset > d->size)
return -EINVAL;
* Following values are allowed to fail, because swapaccount might be
* turned off for current kernel.
*/
- if (wants_swap) {
- memswlimit = get_min_memlimit(cgroup, true);
- if (memswlimit > 0) {
- ret = cgroup_ops->get_memory_swap_current(cgroup_ops, cgroup, &memswusage_str);
- if (ret >= 0 && safe_uint64(memswusage_str, &memswusage, 10) == 0) {
- if (memlimit > memswlimit)
- swtotal = 0;
- else
- swtotal = (memswlimit - memlimit) / 1024;
- if (memusage > memswusage || swtotal == 0)
- swusage = 0;
- else
- swusage = (memswusage - memusage) / 1024;
- }
- }
- }
+ if (wants_swap)
+ get_swap_info(cgroup, memlimit, memusage, &swtotal, &swusage, &memswpriority);
f = fopen_cached("/proc/meminfo", "re", &fopen_cache);
if (!f)
sscanf(line + STRLITERALLEN("SwapTotal:"), "%" PRIu64, &hostswtotal);
+ /* The total amount of swap is always reported to be the
+ lesser of the RAM+SWAP limit or the SWAP device size.
+ This is because the kernel can swap as much as it
+ wants and not only up to swtotal. */
+
+ if (!liblxcfs_memory_is_cgroupv2())
+ swtotal += memlimit;
+
if (hostswtotal < swtotal) {
swtotal = hostswtotal;
- host_swap = true;
+ }
+
+ /* When swappiness is 0, pretend we can't swap. */
+ if (memswpriority == 0) {
+ swtotal = swusage;
}
}
printme = lbuf;
} else if (startswith(line, "SwapFree:")) {
if (wants_swap) {
- uint64_t hostswfree = 0;
-
- if (host_swap) {
- sscanf(line + STRLITERALLEN("SwapFree:"), "%" PRIu64, &hostswfree);
- swfree = hostswfree;
- } else if (swtotal >= swusage) {
- swfree = swtotal - swusage;
- }
+ swfree = swtotal - swusage;
}
snprintf(lbuf, 100, "SwapFree: %8" PRIu64 " kB\n", swfree);
printme = lbuf;
} else if (startswith(line, "Slab:")) {
- snprintf(lbuf, 100, "Slab: %8" PRIu64 " kB\n", (uint64_t)0);
+ snprintf(lbuf, 100, "Slab: %8" PRIu64 " kB\n", (uint64_t)0);
printme = lbuf;
} else if (startswith(line, "Buffers:")) {
snprintf(lbuf, 100, "Buffers: %8" PRIu64 " kB\n", (uint64_t)0);
l = snprintf(cache, cache_size, "%s", printme);
if (l < 0)
return log_error(0, "Failed to write cache");
- if (l >= cache_size)
+ if ((size_t)l >= cache_size)
+ return log_error(0, "Write to cache was truncated");
+
+ cache += l;
+ cache_size -= l;
+ total_len += l;
+ }
+
+ d->cached = 1;
+ d->size = total_len;
+ if (total_len > size)
+ total_len = size;
+ memcpy(buf, d->buf, total_len);
+
+ return total_len;
+}
+
+static int proc_slabinfo_read(char *buf, size_t size, off_t offset,
+ struct fuse_file_info *fi)
+{
+ __do_free char *cgroup = NULL, *line = NULL;
+ __do_free void *fopen_cache = NULL;
+ __do_fclose FILE *f = NULL;
+ __do_close int fd = -EBADF;
+ struct fuse_context *fc = fuse_get_context();
+ struct file_info *d = INTTYPE_TO_PTR(fi->fh);
+ size_t linelen = 0, total_len = 0;
+ char *cache = d->buf;
+ size_t cache_size = d->buflen;
+ pid_t initpid;
+
+ if (offset) {
+ size_t left;
+
+ if (offset > d->size)
+ return -EINVAL;
+
+ if (!d->cached)
+ return 0;
+
+ left = d->size - offset;
+ total_len = left > size ? size : left;
+ memcpy(buf, cache + offset, total_len);
+
+ return total_len;
+ }
+
+ initpid = lookup_initpid_in_store(fc->pid);
+ if (initpid <= 1 || is_shared_pidns(initpid))
+ initpid = fc->pid;
+
+ cgroup = get_pid_cgroup(initpid, "memory");
+ if (!cgroup)
+ return read_file_fuse("/proc/slabinfo", buf, size, d);
+
+ prune_init_slice(cgroup);
+
+ fd = cgroup_ops->get_memory_slabinfo_fd(cgroup_ops, cgroup);
+ if (fd < 0)
+ return read_file_fuse("/proc/slabinfo", buf, size, d);
+
+ f = fdopen_cached(fd, "re", &fopen_cache);
+ if (!f)
+ return read_file_fuse("/proc/slabinfo", buf, size, d);
+
+ while (getline(&line, &linelen, f) != -1) {
+ ssize_t l = snprintf(cache, cache_size, "%s", line);
+ if (l < 0)
+ return log_error(0, "Failed to write cache");
+ if ((size_t)l >= cache_size)
return log_error(0, "Write to cache was truncated");
cache += l;
return read_file_fuse_with_offset(LXC_TYPE_PROC_LOADAVG_PATH,
buf, size, offset, f);
+ case LXC_TYPE_PROC_SLABINFO:
+ if (liblxcfs_functional())
+ return proc_slabinfo_read(buf, size, offset, fi);
+
+ return read_file_fuse_with_offset(LXC_TYPE_PROC_SLABINFO_PATH,
+ buf, size, offset, f);
}
return -EINVAL;