int cached;
};
+struct cpuacct_usage {
+ uint64_t user;
+ uint64_t system;
+};
+
/* The function of hash table.*/
#define LOAD_SIZE 100 /*the size of hash_table */
#define FLUSH_TIME 5 /*the flush rate */
#define EXP_15 2037 /* 1/exp(5sec/15min) */
#define LOAD_INT(x) ((x) >> FSHIFT)
#define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100)
-/*
+/*
* This parameter is used for proc_loadavg_read().
* 1 means use loadavg, 0 means not use.
*/
static int loadavg = 0;
+static volatile sig_atomic_t loadavg_stop = 0;
static int calc_hash(char *name)
{
unsigned int hash = 0;
return g;
}
-void load_free(void)
+static void load_free(void)
{
int i;
struct load_node *f, *p;
FILE *f;
size_t len, ret;
- if (!(f = fdopen(fd, "w")))
+ f = fdopen(fd, "w");
+ if (!f)
return false;
+
len = strlen(string);
ret = fwrite(string, 1, len, f);
if (ret != len) {
- lxcfs_error("Error writing to file: %s\n", strerror(errno));
+ lxcfs_error("%s - Error writing \"%s\" to \"%s\"\n",
+ strerror(errno), string, fnam);
fclose(f);
return false;
}
+
if (fclose(f) < 0) {
- lxcfs_error("Error writing to file: %s\n", strerror(errno));
+ lxcfs_error("%s - Failed to close \"%s\"\n", strerror(errno), fnam);
return false;
}
+
return true;
}
free(f->buf);
f->buf = NULL;
free(f);
+ f = NULL;
}
int cg_releasedir(const char *path, struct fuse_file_info *fi)
static void parse_memstat(char *memstat, unsigned long *cached,
unsigned long *active_anon, unsigned long *inactive_anon,
unsigned long *active_file, unsigned long *inactive_file,
- unsigned long *unevictable)
+ unsigned long *unevictable, unsigned long *shmem)
{
char *eol;
} else if (startswith(memstat, "total_unevictable")) {
sscanf(memstat + 17, "%lu", unevictable);
*unevictable /= 1024;
+ } else if (startswith(memstat, "total_shmem")) {
+ sscanf(memstat + 11, "%lu", shmem);
+ *shmem /= 1024;
}
eol = strchr(memstat, '\n');
if (!eol)
*memswlimit_str = NULL, *memswusage_str = NULL;
unsigned long memlimit = 0, memusage = 0, memswlimit = 0, memswusage = 0,
cached = 0, hosttotal = 0, active_anon = 0, inactive_anon = 0,
- active_file = 0, inactive_file = 0, unevictable = 0,
+ active_file = 0, inactive_file = 0, unevictable = 0, shmem = 0,
hostswtotal = 0;
char *line = NULL;
size_t linelen = 0, total_len = 0, rv = 0;
parse_memstat(memstat_str, &cached, &active_anon,
&inactive_anon, &active_file, &inactive_file,
- &unevictable);
+ &unevictable, &shmem);
f = fopen("/proc/meminfo", "r");
if (!f)
} else if (startswith(line, "SUnreclaim")) {
snprintf(lbuf, 100, "SUnreclaim: %8lu kB\n", 0UL);
printme = lbuf;
+ } else if (startswith(line, "Shmem:")) {
+ snprintf(lbuf, 100, "Shmem: %8lu kB\n", shmem);
+ printme = lbuf;
+ } else if (startswith(line, "ShmemHugePages")) {
+ snprintf(lbuf, 100, "ShmemHugePages: %8lu kB\n", 0UL);
+ printme = lbuf;
+ } else if (startswith(line, "ShmemPmdMapped")) {
+ snprintf(lbuf, 100, "ShmemPmdMapped: %8lu kB\n", 0UL);
+ printme = lbuf;
} else
printme = line;
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 cpucount = get_nprocs();
+ 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;
+
+ if (!cpu_in_cpuset(i, cpuset))
+ continue;
+
+ /* 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;
+
+err:
+ if (usage_str)
+ free(usage_str);
+
+ if (rv != 0) {
+ free(cpu_usage);
+ *return_usage = NULL;
+ }
+
+ 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)
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;
if (offset){
if (offset > d->size)
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) != 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;
int cpu;
char cpu_char[10]; /* That's a lot of cores */
char *c;
+ uint64_t all_used, cg_used, new_idle;
+ int ret;
if (strlen(line) == 0)
continue;
continue;
curcpu ++;
- c = strchr(line, ' ');
- if (!c)
- continue;
- l = snprintf(cache, cache_size, "cpu%d%s", curcpu, c);
- if (l < 0) {
- perror("Error writing to cache");
- rv = 0;
- goto err;
-
- }
- if (l >= cache_size) {
- lxcfs_error("%s\n", "Internal error: truncated write to cache.");
- rv = 0;
- goto err;
- }
-
- cache += l;
- cache_size -= l;
- total_len += l;
-
- if (sscanf(line, "%*s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu",
+ ret = sscanf(line, "%*s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu",
&user,
&nice,
&system,
&softirq,
&steal,
&guest,
- &guest_nice) != 10)
- continue;
- user_sum += user;
- nice_sum += nice;
- system_sum += system;
- idle_sum += idle;
- iowait_sum += iowait;
- irq_sum += irq;
- softirq_sum += softirq;
- steal_sum += steal;
- guest_sum += guest;
- guest_nice_sum += guest_nice;
+ &guest_nice);
+
+ if (ret != 10 || !cg_cpu_usage) {
+ c = strchr(line, ' ');
+ if (!c)
+ continue;
+ l = snprintf(cache, cache_size, "cpu%d%s", curcpu, c);
+ if (l < 0) {
+ perror("Error writing to cache");
+ rv = 0;
+ goto err;
+
+ }
+ if (l >= cache_size) {
+ lxcfs_error("%s\n", "Internal error: truncated write to cache.");
+ rv = 0;
+ goto err;
+ }
+
+ cache += l;
+ cache_size -= l;
+ total_len += l;
+
+ if (ret != 10)
+ continue;
+ }
+
+ if (cg_cpu_usage) {
+ 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) {
+ new_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);
+ new_idle = idle;
+ }
+
+ l = snprintf(cache, cache_size, "cpu%d %lu 0 %lu %lu 0 0 0 0 0 0\n",
+ curcpu, cg_cpu_usage[curcpu].user, cg_cpu_usage[curcpu].system,
+ new_idle);
+
+ if (l < 0) {
+ perror("Error writing to cache");
+ rv = 0;
+ goto err;
+
+ }
+ if (l >= cache_size) {
+ lxcfs_error("%s\n", "Internal error: truncated write to cache.");
+ rv = 0;
+ goto err;
+ }
+
+ cache += l;
+ cache_size -= l;
+ total_len += l;
+
+ user_sum += cg_cpu_usage[curcpu].user;
+ system_sum += cg_cpu_usage[curcpu].system;
+ idle_sum += new_idle;
+
+ } else {
+ user_sum += user;
+ nice_sum += nice;
+ system_sum += system;
+ idle_sum += idle;
+ iowait_sum += iowait;
+ irq_sum += irq;
+ softirq_sum += softirq;
+ steal_sum += steal;
+ guest_sum += guest;
+ guest_nice_sum += guest_nice;
+ }
}
cache = d->buf;
err:
if (f)
fclose(f);
+ if (cg_cpu_usage)
+ free(cg_cpu_usage);
free(line);
free(cpuset);
free(cg);
}
fclose(f);
out:
+ if (line)
+ free(line);
free(path);
return sum;
}
p->last_pid = last_pid;
free(line);
-err_out:
+err_out:
for (; i > 0; i--)
free(idbuf[i-1]);
out:
clock_t time1, time2;
while (1) {
+ if (loadavg_stop == 1)
+ return NULL;
+
time1 = clock();
for (i = 0; i < LOAD_SIZE; i++) {
pthread_mutex_lock(&load_hash[i].lock);
}
}
}
+
+ if (loadavg_stop == 1)
+ return NULL;
+
time2 = clock();
usleep(FLUSH_TIME * 1000000 - (int)((time2 - time1) * 1000000 / CLOCKS_PER_SEC));
}
char *cache = d->buf;
struct load_node *n;
int hash;
- int cfd;
+ int cfd, rv = 0;
unsigned long a, b, c;
if (offset) {
* because delete is not allowed before read has ended.
*/
pthread_rwlock_unlock(&load_hash[hash].rdlock);
- return 0;
+ rv = 0;
+ goto err;
}
do {
n = malloc(sizeof(struct load_node));
pthread_rwlock_unlock(&load_hash[hash].rdlock);
if (total_len < 0 || total_len >= d->buflen) {
lxcfs_error("%s\n", "Failed to write to cache");
- return 0;
+ rv = 0;
+ goto err;
}
d->size = (int)total_len;
d->cached = 1;
if (total_len > size)
total_len = size;
memcpy(buf, d->buf, total_len);
- return total_len;
+ rv = total_len;
+
+err:
+ free(cg);
+ return rv;
}
/* Return a positive number on success, return 0 on failure.*/
pthread_t load_daemon(int load_use)
return pid;
}
+/* Returns 0 on success. */
+int stop_load_daemon(pthread_t pid)
+{
+ int s;
+
+ /* Signal the thread to gracefully stop */
+ loadavg_stop = 1;
+
+ s = pthread_join(pid, NULL); /* Make sure sub thread has been canceled. */
+ if (s != 0) {
+ lxcfs_error("%s\n", "stop_load_daemon error: failed to join");
+ return -1;
+ }
+
+ load_free();
+ loadavg_stop = 0;
+
+ return 0;
+}
+
static off_t get_procfile_size(const char *which)
{
FILE *f = fopen(which, "r");