%PDF- %PDF-
Direktori : /usr/src/blksnap-6.3.0.73/ |
Current File : //usr/src/blksnap-6.3.0.73/log.c |
// SPDX-License-Identifier: GPL-2.0 #define pr_fmt(fmt) KBUILD_MODNAME "-log: " fmt #include <linux/module.h> #include <linux/fs.h> #include <linux/kthread.h> #include <linux/slab.h> #include <linux/sched/task.h> #ifdef STANDALONE_BDEVFILTER #include "blk_snap.h" #else #include <linux/blk_snap.h> #endif #include "version.h" #include "memory_checker.h" #include "log.h" #ifdef BLK_SNAP_FILELOG struct log_request_header { struct timespec64 time; pid_t pid; unsigned level; size_t size; }; #define LOG_REQUEST_BUFFER_SIZE \ (512 - sizeof(struct list_head) - sizeof(struct log_request_header)) struct log_request { struct list_head link; struct log_request_header header; char buffer[LOG_REQUEST_BUFFER_SIZE]; }; #define LOG_REQUEST_POOL_SIZE 128 struct log_request log_requests_pool[LOG_REQUEST_POOL_SIZE]; static int log_level = -1; static char *log_filepath = NULL; static int log_tz_minuteswest = 0; static LIST_HEAD(log_free_requests); static DEFINE_SPINLOCK(log_free_requests_lock); static LIST_HEAD(log_active_requests); static DEFINE_SPINLOCK(log_active_requests_lock); static DECLARE_WAIT_QUEUE_HEAD(log_request_event_add); static struct task_struct* log_task = NULL; static atomic_t log_missed_counter = {0}; void log_init(void) { size_t inx; INIT_LIST_HEAD(&log_free_requests); for (inx=0; inx < LOG_REQUEST_POOL_SIZE; inx++) { struct log_request *rq = &log_requests_pool[inx]; INIT_LIST_HEAD(&rq->link); list_add_tail(&rq->link, &log_free_requests); } } static inline void done_task(void) { struct task_struct* task = log_task; if (!task) return; log_task = NULL; kthread_stop(task); put_task_struct(task); } static inline void done_filepath(void) { if (!log_filepath) return; kfree(log_filepath); memory_object_dec(memory_object_log_filepath); log_filepath = NULL; } void log_done(void) { log_level = -1; done_task(); done_filepath(); } static inline struct log_request *log_request_new(void) { struct log_request *rq; spin_lock(&log_free_requests_lock); rq = list_first_entry_or_null(&log_free_requests, struct log_request, link); if (rq) list_del(&rq->link); else atomic_inc(&log_missed_counter); spin_unlock(&log_free_requests_lock); return rq; } static inline void log_request_free(struct log_request *rq) { INIT_LIST_HEAD(&rq->link); spin_lock(&log_free_requests_lock); list_add_tail(&rq->link, &log_free_requests); spin_unlock(&log_free_requests_lock); } static inline void log_request_push(struct log_request *rq) { INIT_LIST_HEAD(&rq->link); spin_lock(&log_active_requests_lock); list_add_tail(&rq->link, &log_active_requests); spin_unlock(&log_active_requests_lock); } static inline struct log_request *log_request_get(void) { struct log_request *rq; spin_lock(&log_active_requests_lock); rq = list_first_entry_or_null(&log_active_requests, struct log_request, link); if (rq) list_del(&rq->link); spin_unlock(&log_active_requests_lock); return rq; } static inline bool log_request_is_ready(void) { bool ret; spin_lock(&log_active_requests_lock); ret = !list_empty(&log_active_requests); spin_unlock(&log_active_requests_lock); return ret; } #define MAX_PREFIX_SIZE 256 static const char *log_level_text[] = {"EMERG :","ALERT :","CRIT :","ERR :","WRN :","","",""}; static inline void log_request_write(struct file* filp, const struct log_request* rq) { int size; struct tm time; char prefix_buf[MAX_PREFIX_SIZE]; if (!filp) return; if (!rq) return; time64_to_tm(rq->header.time.tv_sec, (-sys_tz.tz_minuteswest + log_tz_minuteswest) * 60, &time); size = snprintf(prefix_buf, MAX_PREFIX_SIZE, "[%02d.%02d.%04ld %02d:%02d:%02d-%06ld] <%d> | %s", time.tm_mday, time.tm_mon + 1, time.tm_year + 1900, time.tm_hour, time.tm_min, time.tm_sec, rq->header.time.tv_nsec / 1000, rq->header.pid, log_level_text[rq->header.level] ); kernel_write(filp, prefix_buf, size, &filp->f_pos); kernel_write(filp, rq->buffer, rq->header.size, &filp->f_pos); } static inline bool log_waiting(void) { int ret; ret = wait_event_interruptible_timeout(log_request_event_add, log_request_is_ready() || kthread_should_stop(), 10 * HZ); return (ret > 0); } static inline void log_request_fill(struct log_request *rq, const int level, const char *fmt, va_list args) { ktime_get_real_ts64(&rq->header.time); rq->header.pid = get_current()->pid; rq->header.level = level; rq->header.size = vscnprintf(rq->buffer, LOG_REQUEST_BUFFER_SIZE, fmt, args); } static inline void log_vprintk_direct(struct file* filp, const int level, const char *fmt, va_list args) { struct log_request rq = {0}; log_request_fill(&rq, level, fmt, args); log_request_write(filp, &rq); } static inline void log_printk_direct(struct file* filp, const int level, const char *fmt, ...) { va_list args; va_start(args, fmt); log_vprintk_direct(filp, level, fmt, args); va_end(args); } static inline struct file* log_reopen(struct file* filp) { if (filp) return filp; if (!log_filepath) return NULL; filp = filp_open(log_filepath, O_WRONLY | O_APPEND | O_CREAT, 0644); if (IS_ERR(filp)) { printk(KERN_ERR pr_fmt("Failed to open file %s\n"), log_filepath); done_filepath(); return NULL; } return filp; } static inline struct file* log_close(struct file* filp) { if (filp) { filp_close(filp, NULL); filp = NULL; } return filp; } static int log_processor(void *data) { int ret = 0; struct log_request *rq; struct file* filp = NULL; int missed; while (!kthread_should_stop()) { missed = atomic_read(&log_missed_counter); if (missed) { atomic_sub(missed, &log_missed_counter); filp = log_reopen(filp); log_printk_direct(filp, LOGLEVEL_INFO, "Missed %d messages\n", missed); } rq = log_request_get(); if (rq) { filp = log_reopen(filp); log_request_write(filp, rq); log_request_free(rq); if (!filp) break; } else if (!log_waiting()) filp = log_close(filp); } filp = log_reopen(filp); while ((rq = log_request_get())) { log_request_write(filp, rq); log_request_free(rq); } log_printk_direct(filp, LOGLEVEL_INFO, "Stop log for module %s\n\n", BLK_SNAP_MODULE_NAME); filp = log_close(filp); return ret; } int log_restart(int level, char *filepath, int tz_minuteswest) { int ret = 0; struct file* filp; struct task_struct* task; if ((level < 0) && !filepath){ /* * Disable logging */ log_done(); return 0; } if (!filepath) return -EINVAL; if (log_filepath && (strcmp(filepath, log_filepath) == 0)) { if (level == log_level) { /* * If the request is executed for the same parameters * that are already set for logging, then logging is * not restarted and an error code EALREADY is returned. */ ret = -EALREADY; } else if (level >= 0) { /* * If only the logging level changes, then * there is no need to restart logging. */ log_level = level; ret = 0; } goto fail; } log_done(); log_init(); filp = filp_open(filepath, O_WRONLY | O_APPEND | O_CREAT, 0644); if (IS_ERR(filp)) { printk(KERN_ERR pr_fmt("Failed to open file %s\n"), filepath); ret = PTR_ERR(filp); goto fail; } ret = kernel_write(filp, "\n", 1, &filp->f_pos); filp_close(filp, NULL); if (ret < 0) { printk(KERN_ERR pr_fmt("Cannot write file %s\n"), filepath); goto fail; } task = kthread_create(log_processor, NULL, "blksnaplog"); if (IS_ERR(task)) { ret = PTR_ERR(task); goto fail; } log_task = get_task_struct(task); log_filepath = filepath; log_level = level <= LOGLEVEL_DEBUG ? level : LOGLEVEL_DEBUG; log_tz_minuteswest = tz_minuteswest; log_printk_direct(filp, LOGLEVEL_INFO, "Start log for module %s version %s loglevel %d\n", BLK_SNAP_MODULE_NAME, VERSION_STR, log_level); wake_up_process(log_task); return 0; fail: kfree(filepath); memory_object_dec(memory_object_log_filepath); return ret; } static void log_vprintk(const int level, const char *fmt, va_list args) { struct log_request *rq; rq = log_request_new(); if (!rq) return; log_request_fill(rq, level, fmt, args); log_request_push(rq); wake_up(&log_request_event_add); } void log_printk(const int level, const char *fmt, ...) { if (level <= log_level) { va_list args; va_start(args, fmt); log_vprintk(level, fmt, args); va_end(args); } } #endif /* BLK_SNAP_FILELOG */