+static uint64_t get_reaper_age(pid_t pid)
+{
+ uint64_t procstart, uptime, procage;
+
+ /* We need to substract the time the process has started since system
+ * boot minus the time when the system has started to get the actual
+ * reaper age.
+ */
+ procstart = get_reaper_start_time_in_sec(pid);
+ procage = procstart;
+ if (procstart > 0) {
+ int ret;
+ struct timespec spec;
+
+ ret = clock_gettime(CLOCK_BOOTTIME, &spec);
+ if (ret < 0)
+ return 0;
+ /* We could make this more precise here by using the tv_nsec
+ * field in the timespec struct and convert it to milliseconds
+ * and then create a double for the seconds and milliseconds but
+ * that seems more work than it is worth.
+ */
+ uptime = spec.tv_sec;
+ procage = uptime - procstart;
+ }
+
+ return procage;
+}
+
+/*
+ * Returns 0 on success.
+ * It is the caller's responsibility to free `return_usage`, unless this
+ * function returns an error.
+ */
+static int read_cpuacct_usage_all(char *cg, char *cpuset, struct cpuacct_usage **return_usage, int *size)
+{
+ int cpucount = get_nprocs_conf();
+ struct cpuacct_usage *cpu_usage;
+ int rv = 0, i, j, ret, read_pos = 0, read_cnt;
+ int cg_cpu;
+ uint64_t cg_user, cg_system;
+ int64_t ticks_per_sec;
+ char *usage_str = NULL;
+
+ ticks_per_sec = sysconf(_SC_CLK_TCK);
+
+ if (ticks_per_sec < 0 && errno == EINVAL) {
+ lxcfs_debug(
+ "%s\n",
+ "read_cpuacct_usage_all failed to determine number of clock ticks "
+ "in a second");
+ return -1;
+ }
+
+ cpu_usage = malloc(sizeof(struct cpuacct_usage) * cpucount);
+ if (!cpu_usage)
+ return -ENOMEM;
+
+ if (!cgfs_get_value("cpuacct", cg, "cpuacct.usage_all", &usage_str)) {
+ rv = -1;
+ goto err;
+ }
+
+ if (sscanf(usage_str, "cpu user system\n%n", &read_cnt) != 0) {
+ lxcfs_error("read_cpuacct_usage_all reading first line from "
+ "%s/cpuacct.usage_all failed.\n", cg);
+ rv = -1;
+ goto err;
+ }
+
+ read_pos += read_cnt;
+
+ for (i = 0, j = 0; i < cpucount; i++) {
+ ret = sscanf(usage_str + read_pos, "%d %lu %lu\n%n", &cg_cpu, &cg_user,
+ &cg_system, &read_cnt);
+
+ if (ret == EOF)
+ break;
+
+ if (ret != 3) {
+ lxcfs_error("read_cpuacct_usage_all reading from %s/cpuacct.usage_all "
+ "failed.\n", cg);
+ rv = -1;
+ goto err;
+ }
+
+ read_pos += read_cnt;
+
+ /* Convert the time from nanoseconds to USER_HZ */
+ cpu_usage[j].user = cg_user / 1000.0 / 1000 / 1000 * ticks_per_sec;
+ cpu_usage[j].system = cg_system / 1000.0 / 1000 / 1000 * ticks_per_sec;
+ j++;
+ }
+
+ rv = 0;
+ *return_usage = cpu_usage;
+ *size = cpucount;
+
+err:
+ if (usage_str)
+ free(usage_str);
+
+ if (rv != 0) {
+ free(cpu_usage);
+ *return_usage = NULL;
+ }
+
+ return rv;
+}
+
+static unsigned long diff_cpu_usage(struct cpuacct_usage *older, struct cpuacct_usage *newer, struct cpuacct_usage *diff, int cpu_count)
+{
+ int i;
+ unsigned long sum = 0;
+
+ for (i = 0; i < cpu_count; i++) {
+ if (!newer[i].online)
+ continue;
+
+ /* When cpuset is changed on the fly, the CPUs might get reordered.
+ * We could either reset all counters, or check that the substractions
+ * below will return expected results.
+ */
+ if (newer[i].user > older[i].user)
+ diff[i].user = newer[i].user - older[i].user;
+ else
+ diff[i].user = 0;
+
+ if (newer[i].system > older[i].system)
+ diff[i].system = newer[i].system - older[i].system;
+ else
+ diff[i].system = 0;
+
+ if (newer[i].idle > older[i].idle)
+ diff[i].idle = newer[i].idle - older[i].idle;
+ else
+ diff[i].idle = 0;
+
+ sum += diff[i].user;
+ sum += diff[i].system;
+ sum += diff[i].idle;
+ }
+
+ return sum;
+}
+
+static void add_cpu_usage(unsigned long *surplus, struct cpuacct_usage *usage, unsigned long *counter, unsigned long threshold)
+{
+ unsigned long free_space, to_add;
+
+ free_space = threshold - usage->user - usage->system;
+
+ if (free_space > usage->idle)
+ free_space = usage->idle;
+
+ to_add = free_space > *surplus ? *surplus : free_space;
+
+ *counter += to_add;
+ usage->idle -= to_add;
+ *surplus -= to_add;
+}
+
+static struct cg_proc_stat *prune_proc_stat_list(struct cg_proc_stat *node)
+{
+ struct cg_proc_stat *first = NULL, *prev, *tmp;
+
+ for (prev = NULL; node; ) {
+ if (!cgfs_param_exist("cpu", node->cg, "cpu.shares")) {
+ tmp = node;
+ lxcfs_debug("Removing stat node for %s\n", node->cg);
+
+ if (prev)
+ prev->next = node->next;
+ else
+ first = node->next;
+
+ node = node->next;
+ free_proc_stat_node(tmp);
+ } else {
+ if (!first)
+ first = node;
+ prev = node;
+ node = node->next;
+ }
+ }
+
+ return first;
+}
+
+#define PROC_STAT_PRUNE_INTERVAL 10
+static void prune_proc_stat_history(void)
+{
+ int i;
+ time_t now = time(NULL);
+
+ for (i = 0; i < CPUVIEW_HASH_SIZE; i++) {
+ pthread_rwlock_wrlock(&proc_stat_history[i]->lock);
+
+ if ((proc_stat_history[i]->lastcheck + PROC_STAT_PRUNE_INTERVAL) > now) {
+ pthread_rwlock_unlock(&proc_stat_history[i]->lock);
+ return;
+ }
+
+ if (proc_stat_history[i]->next) {
+ proc_stat_history[i]->next = prune_proc_stat_list(proc_stat_history[i]->next);
+ proc_stat_history[i]->lastcheck = now;
+ }
+
+ pthread_rwlock_unlock(&proc_stat_history[i]->lock);
+ }
+}
+
+static struct cg_proc_stat *find_proc_stat_node(struct cg_proc_stat_head *head, const char *cg)
+{
+ struct cg_proc_stat *node;
+
+ pthread_rwlock_rdlock(&head->lock);
+
+ if (!head->next) {
+ pthread_rwlock_unlock(&head->lock);
+ return NULL;
+ }
+
+ node = head->next;
+
+ do {
+ if (strcmp(cg, node->cg) == 0)
+ goto out;
+ } while ((node = node->next));
+
+ node = NULL;
+
+out:
+ pthread_rwlock_unlock(&head->lock);
+ prune_proc_stat_history();
+ return node;
+}
+
+static struct cg_proc_stat *new_proc_stat_node(struct cpuacct_usage *usage, int cpu_count, const char *cg)
+{
+ struct cg_proc_stat *node;
+ int i;
+
+ node = malloc(sizeof(struct cg_proc_stat));
+ if (!node)
+ goto err;
+
+ node->cg = NULL;
+ node->usage = NULL;
+ node->view = NULL;
+
+ node->cg = malloc(strlen(cg) + 1);
+ if (!node->cg)
+ goto err;
+
+ strcpy(node->cg, cg);
+
+ node->usage = malloc(sizeof(struct cpuacct_usage) * cpu_count);
+ if (!node->usage)
+ goto err;
+
+ memcpy(node->usage, usage, sizeof(struct cpuacct_usage) * cpu_count);
+
+ node->view = malloc(sizeof(struct cpuacct_usage) * cpu_count);
+ if (!node->view)
+ goto err;
+
+ node->cpu_count = cpu_count;
+ node->next = NULL;
+
+ if (pthread_mutex_init(&node->lock, NULL) != 0) {
+ lxcfs_error("%s\n", "Failed to initialize node lock");
+ goto err;
+ }
+
+ for (i = 0; i < cpu_count; i++) {
+ node->view[i].user = 0;
+ node->view[i].system = 0;
+ node->view[i].idle = 0;
+ }
+
+ return node;
+
+err:
+ if (node && node->cg)
+ free(node->cg);
+ if (node && node->usage)
+ free(node->usage);
+ if (node && node->view)
+ free(node->view);
+ if (node)
+ free(node);
+
+ return NULL;
+}
+
+static struct cg_proc_stat *add_proc_stat_node(struct cg_proc_stat *new_node)
+{
+ int hash = calc_hash(new_node->cg) % CPUVIEW_HASH_SIZE;
+ struct cg_proc_stat_head *head = proc_stat_history[hash];
+ struct cg_proc_stat *node, *rv = new_node;
+
+ pthread_rwlock_wrlock(&head->lock);
+
+ if (!head->next) {
+ head->next = new_node;
+ goto out;
+ }
+
+ node = head->next;
+
+ for (;;) {
+ if (strcmp(node->cg, new_node->cg) == 0) {
+ /* The node is already present, return it */
+ free_proc_stat_node(new_node);
+ rv = node;
+ goto out;
+ }
+
+ if (node->next) {
+ node = node->next;
+ continue;
+ }
+
+ node->next = new_node;
+ goto out;
+ }
+
+out:
+ pthread_rwlock_unlock(&head->lock);
+ return rv;
+}
+
+static bool expand_proc_stat_node(struct cg_proc_stat *node, int cpu_count)
+{
+ struct cpuacct_usage *new_usage, *new_view;
+ int i;
+
+ /* Allocate new memory */
+ new_usage = malloc(sizeof(struct cpuacct_usage) * cpu_count);
+ if (!new_usage)
+ return false;
+
+ new_view = malloc(sizeof(struct cpuacct_usage) * cpu_count);
+ if (!new_view) {
+ free(new_usage);
+ return false;
+ }
+
+ /* Copy existing data & initialize new elements */
+ for (i = 0; i < cpu_count; i++) {
+ if (i < node->cpu_count) {
+ new_usage[i].user = node->usage[i].user;
+ new_usage[i].system = node->usage[i].system;
+ new_usage[i].idle = node->usage[i].idle;
+
+ new_view[i].user = node->view[i].user;
+ new_view[i].system = node->view[i].system;
+ new_view[i].idle = node->view[i].idle;
+ } else {
+ new_usage[i].user = 0;
+ new_usage[i].system = 0;
+ new_usage[i].idle = 0;
+
+ new_view[i].user = 0;
+ new_view[i].system = 0;
+ new_view[i].idle = 0;
+ }
+ }
+
+ free(node->usage);
+ free(node->view);
+
+ node->usage = new_usage;
+ node->view = new_view;
+ node->cpu_count = cpu_count;
+
+ return true;
+}
+
+static struct cg_proc_stat *find_or_create_proc_stat_node(struct cpuacct_usage *usage, int cpu_count, const char *cg)
+{
+ int hash = calc_hash(cg) % CPUVIEW_HASH_SIZE;
+ struct cg_proc_stat_head *head = proc_stat_history[hash];
+ struct cg_proc_stat *node;
+
+ node = find_proc_stat_node(head, cg);
+
+ if (!node) {
+ node = new_proc_stat_node(usage, cpu_count, cg);
+ if (!node)
+ return NULL;
+
+ node = add_proc_stat_node(node);
+ lxcfs_debug("New stat node (%d) for %s\n", cpu_count, cg);
+ }
+
+ pthread_mutex_lock(&node->lock);
+
+ /* If additional CPUs on the host have been enabled, CPU usage counter
+ * arrays have to be expanded */
+ if (node->cpu_count < cpu_count) {
+ lxcfs_debug("Expanding stat node %d->%d for %s\n",
+ node->cpu_count, cpu_count, cg);
+
+ if (!expand_proc_stat_node(node, cpu_count)) {
+ pthread_mutex_unlock(&node->lock);
+ lxcfs_debug("Unable to expand stat node %d->%d for %s\n",
+ node->cpu_count, cpu_count, cg);
+ return NULL;
+ }
+ }
+
+ return node;
+}
+
+static void reset_proc_stat_node(struct cg_proc_stat *node, struct cpuacct_usage *usage, int cpu_count)
+{
+ int i;
+
+ lxcfs_debug("Resetting stat node for %s\n", node->cg);
+ memcpy(node->usage, usage, sizeof(struct cpuacct_usage) * cpu_count);
+
+ for (i = 0; i < cpu_count; i++) {
+ node->view[i].user = 0;
+ node->view[i].system = 0;
+ node->view[i].idle = 0;
+ }
+
+ node->cpu_count = cpu_count;
+}
+
+static int cpuview_proc_stat(const char *cg, const char *cpuset, struct cpuacct_usage *cg_cpu_usage, int cg_cpu_usage_size, FILE *f, char *buf, size_t buf_size)
+{
+ char *line = NULL;
+ size_t linelen = 0, total_len = 0, rv = 0, l;
+ int curcpu = -1; /* cpu numbering starts at 0 */
+ int physcpu, i;
+ int max_cpus = max_cpu_count(cg), cpu_cnt = 0;
+ unsigned long user = 0, nice = 0, system = 0, idle = 0, iowait = 0, irq = 0, softirq = 0, steal = 0, guest = 0, guest_nice = 0;
+ unsigned long user_sum = 0, system_sum = 0, idle_sum = 0;
+ unsigned long user_surplus = 0, system_surplus = 0;
+ unsigned long total_sum, threshold;
+ struct cg_proc_stat *stat_node;
+ struct cpuacct_usage *diff = NULL;
+ int nprocs = get_nprocs_conf();
+
+ if (cg_cpu_usage_size < nprocs)
+ nprocs = cg_cpu_usage_size;
+
+ /* Read all CPU stats and stop when we've encountered other lines */
+ while (getline(&line, &linelen, f) != -1) {
+ int ret;
+ char cpu_char[10]; /* That's a lot of cores */
+ uint64_t all_used, cg_used;
+
+ if (strlen(line) == 0)
+ continue;
+ if (sscanf(line, "cpu%9[^ ]", cpu_char) != 1) {
+ /* not a ^cpuN line containing a number N */
+ break;
+ }
+
+ if (sscanf(cpu_char, "%d", &physcpu) != 1)
+ continue;
+
+ if (physcpu >= cg_cpu_usage_size)
+ continue;
+
+ curcpu ++;
+ cpu_cnt ++;
+
+ if (!cpu_in_cpuset(physcpu, cpuset)) {
+ for (i = curcpu; i <= physcpu; i++) {
+ cg_cpu_usage[i].online = false;
+ }
+ continue;
+ }
+
+ if (curcpu < physcpu) {
+ /* Some CPUs may be disabled */
+ for (i = curcpu; i < physcpu; i++)
+ cg_cpu_usage[i].online = false;
+
+ curcpu = physcpu;
+ }
+
+ cg_cpu_usage[curcpu].online = true;
+
+ ret = sscanf(line, "%*s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu",
+ &user,
+ &nice,
+ &system,
+ &idle,
+ &iowait,
+ &irq,
+ &softirq,
+ &steal,
+ &guest,
+ &guest_nice);
+
+ if (ret != 10)
+ continue;
+
+ all_used = user + nice + system + iowait + irq + softirq + steal + guest + guest_nice;
+ cg_used = cg_cpu_usage[curcpu].user + cg_cpu_usage[curcpu].system;
+
+ if (all_used >= cg_used) {
+ cg_cpu_usage[curcpu].idle = idle + (all_used - cg_used);
+
+ } else {
+ lxcfs_error("cpu%d from %s has unexpected cpu time: %lu in /proc/stat, "
+ "%lu in cpuacct.usage_all; unable to determine idle time\n",
+ curcpu, cg, all_used, cg_used);
+ cg_cpu_usage[curcpu].idle = idle;
+ }
+ }
+
+ /* Cannot use more CPUs than is available due to cpuset */
+ if (max_cpus > cpu_cnt)
+ max_cpus = cpu_cnt;
+
+ stat_node = find_or_create_proc_stat_node(cg_cpu_usage, nprocs, cg);
+
+ if (!stat_node) {
+ lxcfs_error("unable to find/create stat node for %s\n", cg);
+ rv = 0;
+ goto err;
+ }
+
+ diff = malloc(sizeof(struct cpuacct_usage) * nprocs);
+ if (!diff) {
+ rv = 0;
+ goto err;
+ }
+
+ /*
+ * If the new values are LOWER than values stored in memory, it means
+ * the cgroup has been reset/recreated and we should reset too.
+ */
+ for (curcpu = 0; curcpu < nprocs; curcpu++) {
+ if (!cg_cpu_usage[curcpu].online)
+ continue;
+
+ if (cg_cpu_usage[curcpu].user < stat_node->usage[curcpu].user)
+ reset_proc_stat_node(stat_node, cg_cpu_usage, nprocs);
+
+ break;
+ }
+
+ total_sum = diff_cpu_usage(stat_node->usage, cg_cpu_usage, diff, nprocs);
+
+ for (curcpu = 0, i = -1; curcpu < nprocs; curcpu++) {
+ stat_node->usage[curcpu].online = cg_cpu_usage[curcpu].online;
+
+ if (!stat_node->usage[curcpu].online)
+ continue;
+
+ i++;
+
+ stat_node->usage[curcpu].user += diff[curcpu].user;
+ stat_node->usage[curcpu].system += diff[curcpu].system;
+ stat_node->usage[curcpu].idle += diff[curcpu].idle;
+
+ if (max_cpus > 0 && i >= max_cpus) {
+ user_surplus += diff[curcpu].user;
+ system_surplus += diff[curcpu].system;
+ }
+ }
+
+ /* Calculate usage counters of visible CPUs */
+ if (max_cpus > 0) {
+ /* threshold = maximum usage per cpu, including idle */
+ threshold = total_sum / cpu_cnt * max_cpus;
+
+ for (curcpu = 0, i = -1; curcpu < nprocs; curcpu++) {
+ if (i == max_cpus)
+ break;
+
+ if (!stat_node->usage[curcpu].online)
+ continue;
+
+ i++;
+
+ if (diff[curcpu].user + diff[curcpu].system >= threshold)
+ continue;
+
+ /* Add user */
+ add_cpu_usage(
+ &user_surplus,
+ &diff[curcpu],
+ &diff[curcpu].user,
+ threshold);
+
+ if (diff[curcpu].user + diff[curcpu].system >= threshold)
+ continue;
+
+ /* If there is still room, add system */
+ add_cpu_usage(
+ &system_surplus,
+ &diff[curcpu],
+ &diff[curcpu].system,
+ threshold);
+ }
+
+ if (user_surplus > 0)
+ lxcfs_debug("leftover user: %lu for %s\n", user_surplus, cg);
+ if (system_surplus > 0)
+ lxcfs_debug("leftover system: %lu for %s\n", system_surplus, cg);
+
+ for (curcpu = 0, i = -1; curcpu < nprocs; curcpu++) {
+ if (i == max_cpus)
+ break;
+
+ if (!stat_node->usage[curcpu].online)
+ continue;
+
+ i++;
+
+ stat_node->view[curcpu].user += diff[curcpu].user;
+ stat_node->view[curcpu].system += diff[curcpu].system;
+ stat_node->view[curcpu].idle += diff[curcpu].idle;
+
+ user_sum += stat_node->view[curcpu].user;
+ system_sum += stat_node->view[curcpu].system;
+ idle_sum += stat_node->view[curcpu].idle;
+ }
+
+ } else {
+ for (curcpu = 0; curcpu < nprocs; curcpu++) {
+ if (!stat_node->usage[curcpu].online)
+ continue;
+
+ stat_node->view[curcpu].user = stat_node->usage[curcpu].user;
+ stat_node->view[curcpu].system = stat_node->usage[curcpu].system;
+ stat_node->view[curcpu].idle = stat_node->usage[curcpu].idle;
+
+ user_sum += stat_node->view[curcpu].user;
+ system_sum += stat_node->view[curcpu].system;
+ idle_sum += stat_node->view[curcpu].idle;
+ }
+ }
+
+ /* Render the file */
+ /* cpu-all */
+ l = snprintf(buf, buf_size, "cpu %lu 0 %lu %lu 0 0 0 0 0 0\n",
+ user_sum,
+ system_sum,
+ idle_sum);
+
+ if (l < 0) {
+ perror("Error writing to cache");
+ rv = 0;
+ goto err;
+
+ }
+ if (l >= buf_size) {
+ lxcfs_error("%s\n", "Internal error: truncated write to cache.");
+ rv = 0;
+ goto err;
+ }
+
+ buf += l;
+ buf_size -= l;
+ total_len += l;
+
+ /* Render visible CPUs */
+ for (curcpu = 0, i = -1; curcpu < nprocs; curcpu++) {
+ if (!stat_node->usage[curcpu].online)
+ continue;
+
+ i++;
+
+ if (max_cpus > 0 && i == max_cpus)
+ break;
+
+ l = snprintf(buf, buf_size, "cpu%d %lu 0 %lu %lu 0 0 0 0 0 0\n",
+ i,
+ stat_node->view[curcpu].user,
+ stat_node->view[curcpu].system,
+ stat_node->view[curcpu].idle);
+
+ if (l < 0) {
+ perror("Error writing to cache");
+ rv = 0;
+ goto err;
+
+ }
+ if (l >= buf_size) {
+ lxcfs_error("%s\n", "Internal error: truncated write to cache.");
+ rv = 0;
+ goto err;
+ }
+
+ buf += l;
+ buf_size -= l;
+ total_len += l;
+ }
+
+ /* Pass the rest of /proc/stat, start with the last line read */
+ l = snprintf(buf, buf_size, "%s", line);
+
+ if (l < 0) {
+ perror("Error writing to cache");
+ rv = 0;
+ goto err;
+
+ }
+ if (l >= buf_size) {
+ lxcfs_error("%s\n", "Internal error: truncated write to cache.");
+ rv = 0;
+ goto err;
+ }
+
+ buf += l;
+ buf_size -= l;
+ total_len += l;
+
+ /* Pass the rest of the host's /proc/stat */
+ while (getline(&line, &linelen, f) != -1) {
+ l = snprintf(buf, buf_size, "%s", line);
+ if (l < 0) {
+ perror("Error writing to cache");
+ rv = 0;
+ goto err;
+ }
+ if (l >= buf_size) {
+ lxcfs_error("%s\n", "Internal error: truncated write to cache.");
+ rv = 0;
+ goto err;
+ }
+ buf += l;
+ buf_size -= l;
+ total_len += l;
+ }
+
+ rv = total_len;
+
+err:
+ if (stat_node)
+ pthread_mutex_unlock(&stat_node->lock);
+ if (line)
+ free(line);
+ if (diff)
+ free(diff);
+ return rv;
+}
+
+#define CPUALL_MAX_SIZE (BUF_RESERVE_SIZE / 2)
+static int proc_stat_read(char *buf, size_t size, off_t offset,
+ struct fuse_file_info *fi)
+{
+ struct fuse_context *fc = fuse_get_context();
+ struct file_info *d = (struct file_info *)fi->fh;
+ char *cg;
+ char *cpuset = NULL;
+ char *line = NULL;
+ size_t linelen = 0, total_len = 0, rv = 0;
+ int curcpu = -1; /* cpu numbering starts at 0 */
+ int physcpu = 0;
+ unsigned long user = 0, nice = 0, system = 0, idle = 0, iowait = 0, irq = 0, softirq = 0, steal = 0, guest = 0, guest_nice = 0;
+ unsigned long user_sum = 0, nice_sum = 0, system_sum = 0, idle_sum = 0, iowait_sum = 0,
+ irq_sum = 0, softirq_sum = 0, steal_sum = 0, guest_sum = 0, guest_nice_sum = 0;
+ char cpuall[CPUALL_MAX_SIZE];
+ /* reserve for cpu all */
+ char *cache = d->buf + CPUALL_MAX_SIZE;
+ size_t cache_size = d->buflen - CPUALL_MAX_SIZE;
+ FILE *f = NULL;
+ struct cpuacct_usage *cg_cpu_usage = NULL;
+ int cg_cpu_usage_size = 0;
+
+ if (offset){
+ if (offset > d->size)
+ return -EINVAL;
+ if (!d->cached)
+ return 0;
+ int left = d->size - offset;
+ total_len = left > size ? size: left;
+ memcpy(buf, d->buf + offset, total_len);
+ return total_len;
+ }
+
+ pid_t initpid = lookup_initpid_in_store(fc->pid);
+ if (initpid <= 0)
+ initpid = fc->pid;
+ cg = get_pid_cgroup(initpid, "cpuset");
+ if (!cg)
+ return read_file("/proc/stat", buf, size, d);
+ prune_init_slice(cg);
+
+ cpuset = get_cpuset(cg);
+ if (!cpuset)
+ goto err;
+
+ /*
+ * Read cpuacct.usage_all for all CPUs.
+ * If the cpuacct cgroup is present, it is used to calculate the container's
+ * CPU usage. If not, values from the host's /proc/stat are used.
+ */
+ if (read_cpuacct_usage_all(cg, cpuset, &cg_cpu_usage, &cg_cpu_usage_size) != 0) {
+ lxcfs_debug("%s\n", "proc_stat_read failed to read from cpuacct, "
+ "falling back to the host's /proc/stat");
+ }
+
+ f = fopen("/proc/stat", "r");
+ if (!f)
+ goto err;
+
+ //skip first line
+ if (getline(&line, &linelen, f) < 0) {
+ lxcfs_error("%s\n", "proc_stat_read read first line failed.");
+ goto err;
+ }
+
+ if (use_cpuview(cg) && cg_cpu_usage) {
+ total_len = cpuview_proc_stat(cg, cpuset, cg_cpu_usage, cg_cpu_usage_size,
+ f, d->buf, d->buflen);
+ goto out;
+ }