/** @file @brief Linux kernel memory management interface wrapper @details Copyright (c) 2025 Acronis International GmbH @author Denis Kopyrin ([email protected]) @since $Id: $ */ #include "memory.h" #include "compat.h" #include <linux/module.h> #include <linux/kobject.h> struct kmem_cache *g_handles_cache = NULL; memory_metrics_t* g_memory_metrics = NULL; struct metrics_attribute { struct attribute attr; ssize_t (*show)(memory_metrics_t* metrics, char *buf); }; #define METRIC_X \ METRIC(total_file_contexts_tables) \ METRIC(total_transports) \ METRIC(total_transport_events) \ METRIC(total_msgs) \ METRIC(total_msgs_size) \ METRIC(total_sized_msgs) \ METRIC(total_sized_msgs_size) #define METRICS_ATTR_RO(_name) \ static ssize_t _name##_show(memory_metrics_t* metrics, char *buf) \ { \ return sprintf(buf, "%ld\n", (long) atomic64_read(&metrics->_name)); \ } \ static const struct metrics_attribute s_metrics_attr_##_name = __ATTR_RO(_name) #define METRIC(_name) METRICS_ATTR_RO(_name); METRIC_X #undef METRIC static const struct attribute *s_metrics_attrs[] = { #define METRIC(_name) &s_metrics_attr_##_name.attr, METRIC_X #undef METRIC NULL }; #define to_metrics(_at) container_of(to_safe_kobject(_at), memory_metrics_t, skobj) #define to_metrics_attr(_at) container_of(_at, struct metrics_attribute, attr) static ssize_t metrics_show(struct kobject *kobj, struct attribute *attr, char *buf) { memory_metrics_t* metrics = to_metrics(kobj); struct metrics_attribute *metrics_attr = to_metrics_attr(attr); return metrics_attr->show(metrics, buf); } const struct sysfs_ops s_metrics_sysfs_ops = { .show = metrics_show, }; static struct kobj_type s_metrics_ktype = { .release = safe_kobject_sysfs_release, .sysfs_ops = &s_metrics_sysfs_ops, }; static int sysfs_metrics_add(memory_metrics_t *metrics) { return sysfs_create_files(&metrics->skobj.kobj, s_metrics_attrs); } int memory_init(void) { int err; g_memory_metrics = mem_alloc(sizeof(memory_metrics_t)); if (!g_memory_metrics) { return -ENOMEM; } g_handles_cache = kmem_cache_create("fileprotector_file_handle", MAX_HANDLE_SZ, 0, 0, NULL); if (!g_handles_cache) { mem_free(g_memory_metrics); return -ENOMEM; } #define METRIC(_name) atomic64_set(&g_memory_metrics->_name, 0); METRIC_X #undef METRIC safe_kobject_init(&g_memory_metrics->skobj); err = kobject_init_and_add(&g_memory_metrics->skobj.kobj, &s_metrics_ktype, &THIS_MODULE->mkobj.kobj, "metrics"); if (err) { // If 'init_and_add' fails, it really means that 'add' has failed // 'put' without wait will suffice because 'sysfs' does not see metrics yet kobject_put(&g_memory_metrics->skobj.kobj); goto fail; } err = sysfs_metrics_add(g_memory_metrics); if (err) { // Now we are required to use 'safe_kobject_del' because sysfs saw 'add' above. safe_kobject_del(&g_memory_metrics->skobj); goto fail; } return 0; fail: mem_free(g_memory_metrics); kmem_cache_destroy(g_handles_cache); return err; } void memory_deinit(void) { if (g_handles_cache) { kmem_cache_destroy(g_handles_cache); } #ifdef KERNEL_MOCK #define METRIC(_name) BUG_ON(atomic64_read(&g_memory_metrics->_name)); METRIC_X #undef METRIC #endif if (g_memory_metrics) { sysfs_remove_files(&g_memory_metrics->skobj.kobj, s_metrics_attrs); safe_kobject_del(&g_memory_metrics->skobj); mem_free(g_memory_metrics); } }