Merge pull request #11530 from keszybz/journal-cache-trimming

Journal cache trimming
This commit is contained in:
Lennart Poettering
2019-01-26 13:55:55 +01:00
committed by GitHub
10 changed files with 99 additions and 17 deletions
+3 -4
View File
@@ -259,15 +259,14 @@ int prioq_reshuffle(Prioq *q, void *data, unsigned *idx) {
return 1;
}
void *prioq_peek(Prioq *q) {
void *prioq_peek_by_index(Prioq *q, unsigned idx) {
if (!q)
return NULL;
if (q->n_items <= 0)
if (idx >= q->n_items)
return NULL;
return q->items[0].data;
return q->items[idx].data;
}
void *prioq_pop(Prioq *q) {
+7 -1
View File
@@ -19,8 +19,14 @@ int prioq_put(Prioq *q, void *data, unsigned *idx);
int prioq_remove(Prioq *q, void *data, unsigned *idx);
int prioq_reshuffle(Prioq *q, void *data, unsigned *idx);
void *prioq_peek(Prioq *q) _pure_;
void *prioq_peek_by_index(Prioq *q, unsigned idx) _pure_;
static inline void *prioq_peek(Prioq *q) {
return prioq_peek_by_index(q, 0);
}
void *prioq_pop(Prioq *q);
#define PRIOQ_FOREACH_ITEM(q, p) \
for (unsigned _i = 0; (p = prioq_peek_by_index(q, _i)); _i++)
unsigned prioq_size(Prioq *q) _pure_;
bool prioq_isempty(Prioq *q) _pure_;
+5 -4
View File
@@ -201,13 +201,11 @@ int procfs_cpu_get_usage(nsec_t *ret) {
return 0;
}
int procfs_memory_get_current(uint64_t *ret) {
int procfs_memory_get(uint64_t *ret_total, uint64_t *ret_used) {
uint64_t mem_total = UINT64_MAX, mem_free = UINT64_MAX;
_cleanup_fclose_ FILE *f = NULL;
int r;
assert(ret);
f = fopen("/proc/meminfo", "re");
if (!f)
return -errno;
@@ -262,6 +260,9 @@ int procfs_memory_get_current(uint64_t *ret) {
if (mem_free > mem_total)
return -EINVAL;
*ret = (mem_total - mem_free) * 1024U;
if (ret_total)
*ret_total = mem_total * 1024U;
if (ret_used)
*ret_used = (mem_total - mem_free) * 1024U;
return 0;
}
+4 -1
View File
@@ -11,4 +11,7 @@ int procfs_tasks_get_current(uint64_t *ret);
int procfs_cpu_get_usage(nsec_t *ret);
int procfs_memory_get_current(uint64_t *ret);
int procfs_memory_get(uint64_t *ret_total, uint64_t *ret_used);
static inline int procfs_memory_get_used(uint64_t *ret) {
return procfs_memory_get(NULL, ret);
}
+1 -1
View File
@@ -291,7 +291,7 @@ static int process(
} else if (streq(controller, "memory")) {
if (is_root_cgroup(path)) {
r = procfs_memory_get_current(&g->memory);
r = procfs_memory_get_used(&g->memory);
if (r < 0)
return r;
} else {
+1 -1
View File
@@ -2780,7 +2780,7 @@ int unit_get_memory_current(Unit *u, uint64_t *ret) {
/* The root cgroup doesn't expose this information, let's get it from /proc instead */
if (unit_has_host_root_cgroup(u))
return procfs_memory_get_current(ret);
return procfs_memory_get_used(ret);
if ((u->cgroup_realized_mask & CGROUP_MASK_MEMORY) == 0)
return -ENODATA;
+59 -4
View File
@@ -16,6 +16,7 @@
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
#include "procfs-util.h"
#include "string-util.h"
#include "syslog-util.h"
#include "unaligned.h"
@@ -60,7 +61,37 @@
/* Keep at most 16K entries in the cache. (Note though that this limit may be violated if enough streams pin entries in
* the cache, in which case we *do* permit this limit to be breached. That's safe however, as the number of stream
* clients itself is limited.) */
#define CACHE_MAX (16*1024)
#define CACHE_MAX_FALLBACK 128U
#define CACHE_MAX_MAX (16*1024U)
#define CACHE_MAX_MIN 64U
static size_t cache_max(void) {
static size_t cached = -1;
if (cached == (size_t) -1) {
uint64_t mem_total;
int r;
r = procfs_memory_get(&mem_total, NULL);
if (r < 0) {
log_warning_errno(r, "Cannot query /proc/meminfo for MemTotal: %m");
cached = CACHE_MAX_FALLBACK;
} else {
/* Cache entries are usually a few kB, but the process cmdline is controlled by the
* user and can be up to _SC_ARG_MAX, usually 2MB. Let's say that approximately up to
* 1/8th of memory may be used by the cache.
*
* In the common case, this formula gives 64 cache entries for each GB of RAM.
*/
long l = sysconf(_SC_ARG_MAX);
assert(l > 0);
cached = CLAMP(mem_total / 8 / (uint64_t) l, CACHE_MAX_MIN, CACHE_MAX_MAX);
}
}
return cached;
}
static int client_context_compare(const void *a, const void *b) {
const ClientContext *x = a, *y = b;
@@ -550,15 +581,39 @@ refresh:
}
static void client_context_try_shrink_to(Server *s, size_t limit) {
ClientContext *c;
usec_t t;
assert(s);
/* Flush any cache entries for PIDs that have already moved on. Don't do this
* too often, since it's a slow process. */
t = now(CLOCK_MONOTONIC);
if (s->last_cache_pid_flush + MAX_USEC < t) {
unsigned n = prioq_size(s->client_contexts_lru), idx = 0;
/* We do a number of iterations based on the initial size of the prioq. When we remove an
* item, a new item is moved into its places, and items to the right might be reshuffled.
*/
for (unsigned i = 0; i < n; i++) {
c = prioq_peek_by_index(s->client_contexts_lru, idx);
assert(c->n_ref == 0);
if (!pid_is_unwaited(c->pid))
client_context_free(s, c);
else
idx ++;
}
s->last_cache_pid_flush = t;
}
/* Bring the number of cache entries below the indicated limit, so that we can create a new entry without
* breaching the limit. Note that we only flush out entries that aren't pinned here. This means the number of
* cache entries may very well grow beyond the limit, if all entries stored remain pinned. */
while (hashmap_size(s->client_contexts) > limit) {
ClientContext *c;
c = prioq_pop(s->client_contexts_lru);
if (!c)
break; /* All remaining entries are pinned, give up */
@@ -627,7 +682,7 @@ static int client_context_get_internal(
return 0;
}
client_context_try_shrink_to(s, CACHE_MAX-1);
client_context_try_shrink_to(s, cache_max()-1);
r = client_context_new(s, pid, &c);
if (r < 0)
+2
View File
@@ -161,6 +161,8 @@ struct Server {
Hashmap *client_contexts;
Prioq *client_contexts_lru;
usec_t last_cache_pid_flush;
ClientContext *my_context; /* the context of journald itself */
ClientContext *pid1_context; /* the context of PID 1 */
};
+16
View File
@@ -69,6 +69,11 @@ static void test_struct(void) {
assert_se(q = prioq_new((compare_func_t) test_compare));
assert_se(s = set_new(&test_hash_ops));
assert_se(prioq_peek(q) == NULL);
assert_se(prioq_peek_by_index(q, 0) == NULL);
assert_se(prioq_peek_by_index(q, 1) == NULL);
assert_se(prioq_peek_by_index(q, (unsigned) -1) == NULL);
for (i = 0; i < SET_SIZE; i++) {
assert_se(t = new0(struct test, 1));
t->value = (unsigned) rand();
@@ -79,6 +84,17 @@ static void test_struct(void) {
assert_se(set_consume(s, t) >= 0);
}
for (i = 0; i < SET_SIZE; i++)
assert_se(prioq_peek_by_index(q, i));
assert_se(prioq_peek_by_index(q, SET_SIZE) == NULL);
unsigned count = 0;
PRIOQ_FOREACH_ITEM(q, t) {
assert_se(t);
count++;
}
assert_se(count == SET_SIZE);
while ((t = set_steal_first(s))) {
assert_se(prioq_remove(q, t, &t->idx) == 1);
assert_se(prioq_remove(q, t, &t->idx) == 0);
+1 -1
View File
@@ -18,7 +18,7 @@ int main(int argc, char *argv[]) {
assert_se(procfs_cpu_get_usage(&nsec) >= 0);
log_info("Current system CPU time: %s", format_timespan(buf, sizeof(buf), nsec/NSEC_PER_USEC, 1));
assert_se(procfs_memory_get_current(&v) >= 0);
assert_se(procfs_memory_get_used(&v) >= 0);
log_info("Current memory usage: %s", format_bytes(buf, sizeof(buf), v));
assert_se(procfs_tasks_get_current(&v) >= 0);