%PDF- %PDF-
 Mini Shell
 Mini Shell  
 | 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 */