%PDF- %PDF-
| Direktori : /var/lib/dkms/blksnap/6.3.0.73/source/ |
| Current File : //var/lib/dkms/blksnap/6.3.0.73/source/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 */