X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=bindings.c;h=70386fc75f2804b91d9dd77d4d006421b35ab1c6;hb=a95d68e5ffad31961140e8fcafa156f794e32bb9;hp=b20eaa053b92877683163a27206143c8c9eed36a;hpb=9c480eb7bc9e395fe150ba636837a94f0b75f723;p=mirror_lxcfs.git diff --git a/bindings.c b/bindings.c index b20eaa0..70386fc 100644 --- a/bindings.c +++ b/bindings.c @@ -80,6 +80,11 @@ struct file_info { 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 */ @@ -92,11 +97,12 @@ struct file_info { #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; @@ -247,7 +253,7 @@ static struct load_node *del_node(struct load_node *n, int locate) return g; } -void load_free(void) +static void load_free(void) { int i; struct load_node *f, *p; @@ -578,19 +584,24 @@ static bool write_string(const char *fnam, const char *string, int fd) 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; } @@ -2043,6 +2054,7 @@ static void do_release_file_info(struct fuse_file_info *fi) free(f->buf); f->buf = NULL; free(f); + f = NULL; } int cg_releasedir(const char *path, struct fuse_file_info *fi) @@ -3156,7 +3168,7 @@ static bool startswith(const char *line, const char *pref) 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; @@ -3179,6 +3191,9 @@ static void parse_memstat(char *memstat, unsigned long *cached, } 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) @@ -3295,7 +3310,7 @@ static int proc_meminfo_read(char *buf, size_t size, off_t offset, *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; @@ -3346,7 +3361,7 @@ static int proc_meminfo_read(char *buf, size_t size, off_t offset, parse_memstat(memstat_str, &cached, &active_anon, &inactive_anon, &active_file, &inactive_file, - &unevictable); + &unevictable, &shmem); f = fopen("/proc/meminfo", "r"); if (!f) @@ -3422,6 +3437,15 @@ static int proc_meminfo_read(char *buf, size_t size, off_t offset, } 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; @@ -3793,6 +3817,89 @@ static uint64_t get_reaper_age(pid_t pid) 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) @@ -3812,6 +3919,7 @@ static int proc_stat_read(char *buf, size_t size, off_t offset, 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) @@ -3836,6 +3944,16 @@ static int proc_stat_read(char *buf, size_t size, off_t offset, 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; @@ -3851,6 +3969,8 @@ static int proc_stat_read(char *buf, size_t size, off_t offset, 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; @@ -3879,27 +3999,7 @@ static int proc_stat_read(char *buf, size_t size, off_t offset, 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, @@ -3909,18 +4009,83 @@ static int proc_stat_read(char *buf, size_t size, off_t offset, &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; @@ -3958,6 +4123,8 @@ static int proc_stat_read(char *buf, size_t size, off_t offset, err: if (f) fclose(f); + if (cg_cpu_usage) + free(cg_cpu_usage); free(line); free(cpuset); free(cg); @@ -4372,6 +4539,8 @@ static int calc_pid(char ***pid_buf, char *dpath, int depth, int sum, int cfd) } fclose(f); out: + if (line) + free(line); free(path); return sum; } @@ -4480,7 +4649,7 @@ static int refresh_load(struct load_node *p, char *path) p->last_pid = last_pid; free(line); -err_out: +err_out: for (; i > 0; i--) free(idbuf[i-1]); out: @@ -4500,6 +4669,9 @@ void *load_begin(void *arg) 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); @@ -4536,6 +4708,10 @@ out: f = f->next; } } } + + if (loadavg_stop == 1) + return NULL; + time2 = clock(); usleep(FLUSH_TIME * 1000000 - (int)((time2 - time1) * 1000000 / CLOCKS_PER_SEC)); } @@ -4552,7 +4728,7 @@ static int proc_loadavg_read(char *buf, size_t size, off_t offset, char *cache = d->buf; struct load_node *n; int hash; - int cfd; + int cfd, rv = 0; unsigned long a, b, c; if (offset) { @@ -4587,7 +4763,8 @@ static int proc_loadavg_read(char *buf, size_t size, off_t 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)); @@ -4617,7 +4794,8 @@ static int proc_loadavg_read(char *buf, size_t size, off_t offset, 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; @@ -4625,7 +4803,11 @@ static int proc_loadavg_read(char *buf, size_t size, off_t offset, 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) @@ -4649,6 +4831,26 @@ 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");