From: Arkadiusz Nowak Date: Tue, 21 Jun 2022 11:14:50 +0000 (+0200) Subject: Include zero-copy logging kernel module X-Git-Tag: submit/tizen/20220906.135332~9 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=a5393d58408e5be4b6bb5c26ca96a53175d22fee;p=platform%2Fkernel%2Flinux-tizen-modules-source.git Include zero-copy logging kernel module This is basically borrowed from the Tizen TV, where the kernel module was called vlog. Hence the attribution in the MODULE_AUTHOR field. In our version, the module is called zlogger instead. There is also a number of changes from the original: - we support direct writing to the module (similar to the stdout redirection support in the logger module), which includes the write operation, various ioctl commands and the test vlog_stdout program, - the thread block table (g_threads) works slightly differently. It is no longer a table, but a hashtable. This also removes a limitation on the max TID (previously such a limitation has been needed, as the table was indexed using the TID), - the thread block table is no longer mmapable. Instead, the userspace is supposed to cache the received block number using thread-local storage, and check if it still has the block by verifying the TID field in the block header, which is reset when the block is lost. Change-Id: Ia04f1add0a4904bfd92b156022c704bc62495931 --- diff --git a/include/uapi/linux/zlogger.h b/include/uapi/linux/zlogger.h new file mode 100644 index 0000000..9cd11c5 --- /dev/null +++ b/include/uapi/linux/zlogger.h @@ -0,0 +1,8 @@ +#ifndef _ZLOGGER_H +#define _ZLOGGER_H + +#define IOCTL_COMMAND_ALLOC (20745321) +#define SET_DEFAULT_PRIORITY _IOW('a','a', char*) +#define SET_DEFAULT_TAG _IOW('a','b', char*) + +#endif diff --git a/kernel/Makefile b/kernel/Makefile index a4a8d16..d3d3a64 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -20,10 +20,12 @@ CFLAGS_proc-tsm.o += -Wno-error=missing-attributes BUILD_logger ?= n BUILD_proc_tsm ?= n BUILD_kdbus ?= n +BUILD_zlogger ?= n obj-$(BUILD_logger) += logger.o obj-$(BUILD_proc_tsm) += proc-tsm.o obj-$(BUILD_kdbus) += kdbus/ +obj-$(BUILD_zlogger) += zlogger/ all: make -C $(KERNELDIR) M=$(PWD) CFLAGS_MODULE=-I$(PWD)/include modules diff --git a/kernel/zlogger/Makefile b/kernel/zlogger/Makefile new file mode 100644 index 0000000..3261066 --- /dev/null +++ b/kernel/zlogger/Makefile @@ -0,0 +1,2 @@ +ccflags-y += -std=c11 -std=gnu11 -Wno-error=missing-attributes -Wno-error=declaration-after-statement +obj-m := zlogger.o diff --git a/kernel/zlogger/zlogger.c b/kernel/zlogger/zlogger.c new file mode 100644 index 0000000..2ccc100 --- /dev/null +++ b/kernel/zlogger/zlogger.c @@ -0,0 +1,875 @@ +/* + * drivers/misc/zlogger.c + * + * A Logging Subsystem for Tizen TV + * + * Copyright (C) 2021 Samsung Electronics Co., Ltd + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "zlogger: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MB (1 << 20) +#define KB (1 << 10) + +#define DEVICE_COUNT (8) +#define MAP_SIZE (4 * MB) +#define BUFFER_SIZE (DEVICE_COUNT * MAP_SIZE) +#define V_BLOCK_SIZE (2 * KB) +#define BLOCK_COUNT (BUFFER_SIZE / V_BLOCK_SIZE) +#define DATA_MAX (V_BLOCK_SIZE - sizeof(struct head_t)) +#define MS_PER_SEC (1000) +#define NS_PER_SEC (1000000000UL) + +#define ZLOGGER_DEVICE_NAME "zlogger" +#define ZLOGGER_SMACK_LABEL "*" + +#define MAX_TAG_SIZE (32) +#define MAX_MSG_SIZE (140) + +#define BLOCK_RATIO(count) (count*100/BLOCK_COUNT) + +#define ZLOG_FD_BUFER (2 * MB) + +struct thread_t { + uint16_t block; +}; + +struct entry_t { + uint64_t time; + uint16_t CRC; + uint16_t len; + char data[0]; +}; + +struct head_t { + uint64_t ts; + pid_t pid; + pid_t tid; + uint16_t offset; +}; + +struct block_t { + struct head_t head; + char data[DATA_MAX]; +}; + +struct queue_t { + char name[5]; + uint16_t front; + uint16_t rear; + uint16_t count; + uint16_t capacity; + uint16_t *values; +}; + +struct thread_table_field { + pid_t tid; + uint16_t blk; + bool is_stdout; + struct hlist_node next; +}; + +struct thread_table { + DECLARE_HASHTABLE(data, 16); +}; + +// taken from dlog.h +typedef enum { + DLOG_UNKNOWN = 0, /**< Keep this always at the start */ + DLOG_DEFAULT, /**< Default */ + DLOG_VERBOSE, /**< Verbose */ + DLOG_DEBUG, /**< Debug */ + DLOG_INFO, /**< Info */ + DLOG_WARN, /**< Warning */ + DLOG_ERROR, /**< Error */ + DLOG_FATAL, /**< Fatal */ + DLOG_SILENT, /**< Silent */ + DLOG_PRIO_MAX /**< Keep this always at the end. */ +} log_priority; + +/*zlogger file channel */ + +#define LOG_BUFFER_SIZE 512 +const char default_tag[] = "STDOUT"; +typedef struct { + char prio; + char tag[MAX_TAG_SIZE]; + char msg[MAX_MSG_SIZE]; + char *buffer; + size_t buffer_len; +} zlog_file_t; + +/*--zlogger file channel*/ + +static struct miscdevice zlogger_device; + +static int g_init; +static char *g_shm_ptr[DEVICE_COUNT]; +static struct thread_table *g_thread_table; + +static struct mutex g_block_mutex; +static struct mutex g_task_mutex; + +static struct queue_t g_free_q; + +static int g_max_thread_id; + +static uint64_t g_start_time; +static uint64_t g_hwc_offset; + +static int g_task_on; +static uint32_t g_free_count; + +static uint32_t g_err_count; + +static struct completion g_completion; + +static int g_zlog_enable = 1; +module_param_named(zlog_enable, g_zlog_enable, int, 0644); + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,17,0)) +static inline u64 ktime_get_ns(void) +{ + return ktime_to_ns(ktime_get()); +} +#endif + +static uint16_t get_thread_table(pid_t tid, bool is_stdout) +{ + struct thread_table_field *ptr = NULL; + + // TODO: Is this needed? + if (g_thread_table == NULL) + return 0; + + hash_for_each_possible(g_thread_table->data, ptr, next, tid) { + if (ptr->tid == tid && ptr->is_stdout == is_stdout) { + return ptr->blk; + } + } + + return 0; +} + +static void set_thread_table(pid_t tid, bool is_stdout, uint16_t blk) +{ + struct thread_table_field *ptr = NULL; + + // TODO: Is this needed? + if (g_thread_table == NULL) + return; + + hash_for_each_possible(g_thread_table->data, ptr, next, tid) { + if (ptr->tid == tid && ptr->is_stdout == is_stdout) { + ptr->blk = blk; + return; + } + } + + ptr = kzalloc(sizeof(*ptr), GFP_KERNEL); + ptr->tid = tid; + ptr->is_stdout = is_stdout; + ptr->blk = blk; + hash_add(g_thread_table->data, &ptr->next, tid); +} + +static inline char *get_shared_memory(int dev_index) +{ + if (dev_index < 0 || dev_index >= DEVICE_COUNT) { + pr_debug("Invalid index: %d\n", dev_index); + return NULL; + } + + return g_shm_ptr[dev_index]; +} + +static inline struct block_t *get_block(uint16_t block_index) +{ + uint16_t index = block_index - 1; + int offset = index & (V_BLOCK_SIZE - 1); + char *p = get_shared_memory(index / V_BLOCK_SIZE); + + if (!p) + return NULL; + + return (struct block_t *)(p + (offset * V_BLOCK_SIZE)); +} + +static inline void queue_init(struct queue_t *q, const char *name, uint16_t capacity) +{ + snprintf(q->name, sizeof(q->name), "%s", name); + q->front = 0; + q->rear = capacity - 1; + q->count = 0; + q->capacity = capacity; + q->values = kzalloc(capacity * sizeof(uint16_t), GFP_KERNEL); +} + +static inline void queue_deinit(struct queue_t *q) +{ + if (q->values) + kfree(q->values); +} + +static inline uint16_t queue_pop(struct queue_t *q) +{ + uint16_t r; + + if (q->count == 0) + return 0; + + r = q->values[q->front++]; + if (q->front == q->capacity) + q->front = 0; + q->count--; + + return r; +} + +static inline void queue_push(struct queue_t *q, uint16_t value) +{ + if (q->count >= q->capacity) { + pr_info("[%s] Queue is full", q->name); + return; + } + + if (value == 0) { + pr_info("[%s] NULL is invalid", q->name); + return; + } + + q->rear++; + if (q->rear == q->capacity) + q->rear = 0; + q->values[q->rear] = value; + q->count++; +} + +static int zlog_task(void *user_data) +{ + int i; + int is_stdout; + uint16_t blk; + + do { + // TODO: Consider hash_for_each_safe + for (i = 1; i <= g_max_thread_id; i++) { + for (is_stdout = 0; is_stdout <= 1; ++is_stdout) { + blk = get_thread_table(i, is_stdout); + // TODO: g_start_time should be under some kind of mutex. + if (blk && get_block(blk)->head.ts < g_start_time) { + mutex_lock(&g_block_mutex); + get_block(blk)->head.tid = 0; + queue_push(&g_free_q, blk); + set_thread_table(i, is_stdout, 0); + // TODO: The userspace might very well be using this block right now. + mutex_unlock(&g_block_mutex); + g_free_count++; + } + } + } + + // TODO: This makes no sense. + wait_for_completion_interruptible_timeout(&g_completion, msecs_to_jiffies(MS_PER_SEC * 5)); + } while (1); + + g_task_on = 0; + do_exit(0); + + return 0; +} + +static void run_task(void) +{ + struct task_struct *task; + + if (!mutex_trylock(&g_task_mutex)) + return; + + if (g_task_on) { + // TODO: this mutex is either incorrect or nonsensical, + // since the other task will not end (it is an infinite loop after all). + mutex_unlock(&g_task_mutex); + complete(&g_completion); + return; + } + g_task_on = 1; + mutex_unlock(&g_task_mutex); + + task = kthread_run(zlog_task, NULL, "zlog_task"); + if (IS_ERR(task)) { + pr_err("Failed to run zlog_task\n"); + g_task_on = 0; + } +} + +static long alloc_block_for_thread(bool is_stdout) +{ + pid_t pid = current->tgid; + pid_t tid = current->pid; + uint16_t blk; + struct block_t *block; + + if (g_max_thread_id < tid) + g_max_thread_id = tid; + + mutex_lock(&g_block_mutex); + blk = get_thread_table(tid, is_stdout); + if (blk) + queue_push(&g_free_q, blk); + blk = queue_pop(&g_free_q); + set_thread_table(tid, is_stdout, blk); + + if (!blk) { + if ((g_err_count++ % 10000) < 3) + pr_info("[NO MEMORY] tid:%d free:%d err:%d", tid, g_free_q.count, g_err_count); + mutex_unlock(&g_block_mutex); + return -ENOMEM; + } + + block = get_block(blk); + + // TODO: Needs documentation on how the g_start_time value behaves. + if (g_start_time < block->head.ts) + g_start_time = block->head.ts; + + block->head.pid = pid; + block->head.tid = tid; + block->head.offset = 0; + block->head.ts = g_start_time; + mutex_unlock(&g_block_mutex); + + return (long)blk; +} + +static inline struct block_t *get_valid_block(int tid, size_t len, bool is_stdout) +{ + uint16_t blk = 0; + long r; + + blk = get_thread_table(tid, is_stdout); + + if (blk != 0) { + struct block_t *block = get_block(blk); + + if (!block) + return NULL; + + if (block->head.offset + len < DATA_MAX) + return block; + } + + r = alloc_block_for_thread(is_stdout); + if (r <= 0) + return NULL; + + return get_block((uint16_t)r); +} + +static int zlogger_open(struct inode *inode, struct file *file) +{ + int ret = nonseekable_open(inode, file); + if (ret) + return ret; + file->private_data = NULL; + return 0; +} + +static int zlogger_release(struct inode *ignored, struct file *file) +{ + if (file->private_data != NULL) { + zlog_file_t *zlog_file_data = (zlog_file_t *)file->private_data; + + if (zlog_file_data->buffer != NULL) { + kfree(zlog_file_data->buffer); + zlog_file_data->buffer = NULL; + } + kfree(file->private_data); + file->private_data = NULL; + } + return 0; +} + +static int zlogger_mmap(struct file *filep, struct vm_area_struct *vma) +{ + const int PAGES_PER_MAP = MAP_SIZE / PAGE_SIZE; + int dev_index = (int)vma->vm_pgoff / PAGES_PER_MAP; + unsigned long offset = vma->vm_pgoff % PAGES_PER_MAP; + unsigned long size = vma->vm_end - vma->vm_start; + char *p; + struct page *page; + + if (dev_index > DEVICE_COUNT || offset != 0 || size > MAP_SIZE) { + pr_err("mmap failed: dev(%d) offset(%lu), size(%lu), pgoff(%lu)\n", dev_index, offset, size, vma->vm_pgoff); + return -EINVAL; + } + + p = get_shared_memory(dev_index); + if (p) + page = virt_to_page((unsigned long)p); + else + return -EINVAL; + + return remap_pfn_range(vma, vma->vm_start, page_to_pfn(page), size, vma->vm_page_prot); +} + +static ssize_t zlogger_read(struct file *filep, char *buffer, size_t len, loff_t *offset) +{ + pr_err("read failed!\n"); + return -EPERM; +} + +static ssize_t init_file_zlog_tag_data(struct file *filep) +{ + zlog_file_t *zlog_file_data = kmalloc(sizeof(zlog_file_t), GFP_KERNEL); + if (!zlog_file_data) { + pr_err("init_zlog_tag_data: no memory \n"); + return -ENOMEM; + } + zlog_file_data->buffer = NULL; + memcpy(zlog_file_data->tag, default_tag, sizeof(default_tag) - 1); + zlog_file_data->tag[sizeof(default_tag) - 1] = '\0'; + zlog_file_data->prio = (char)DLOG_INFO; + zlog_file_data->buffer = kmalloc(LOG_BUFFER_SIZE, GFP_KERNEL); + + if (!zlog_file_data->buffer) { + kfree(zlog_file_data); + return -ENOMEM; + } + zlog_file_data->buffer_len = LOG_BUFFER_SIZE; + filep->private_data = (void *)zlog_file_data; + return 0; +} + +int update_zlog_data_buffer_size(zlog_file_t *zlog_file_data, size_t len) +{ + char *new_buffer = kmalloc(len, GFP_KERNEL); + if (!new_buffer) + return -ENOMEM; + kfree(zlog_file_data->buffer); + zlog_file_data->buffer = new_buffer; + zlog_file_data->buffer_len = len; + return 0; +} + +static int _zlog_write(const unsigned char prio, const char *tag, const char *msg) +{ + uint64_t ts = ktime_get_ns(); + uint16_t *slt = (uint16_t *)&ts; + const pid_t tid = current->pid; + size_t hd_size = sizeof(struct entry_t); + size_t prio_size = 1; + size_t tag_size = strnlen(tag, MAX_TAG_SIZE) + 1; + size_t msg_size = strnlen(msg, MAX_MSG_SIZE) + 1; + size_t entry_size = hd_size + prio_size + tag_size + msg_size; + struct block_t *block = get_valid_block(tid, entry_size, true); + struct entry_t *entry; + struct entry_t tmp; + + if (block == NULL) { + pr_err("_zlog_write: no memory\n"); + return -ENOMEM; + } + + entry = (struct entry_t *)(block->data + block->head.offset); + + if ((char *)entry + entry_size > (char *)block + V_BLOCK_SIZE) { + // TODO: This is not unexpected: the offset can change at any time, + // as it is userspace writable. + pr_err("[%d] block:%p(tid:%d offset:%x) entry:%p(%zu)\n", + tid, block, block->head.tid, block->head.offset, entry, entry_size); + return -EFAULT; + } + + tmp.time = ts; + tmp.CRC = slt[0] + slt[1] + slt[2] + slt[3]; + tmp.len = (uint16_t)entry_size - hd_size; + + memcpy(entry, &tmp, hd_size); + entry->data[0] = prio; + memcpy(&entry->data[prio_size], tag, tag_size-1); + entry->data[prio_size+tag_size-1] = 0; + memcpy(&entry->data[prio_size+tag_size], msg, msg_size-1); + entry->data[prio_size+tag_size+msg_size-1] = 0; + block->head.offset += (uint16_t)entry_size; + block->head.ts = ts; + + return (int)entry_size; +} + +static void endl_to_zero(zlog_file_t *zlog_file_data, size_t len) +{ + char *cb = (char*)zlog_file_data->buffer; + while(cb < (char*)zlog_file_data->buffer + len) { + if(*cb == '\n') { + *cb = '\0'; + } + cb++; + } +} + +static ssize_t partition_write_buffer(zlog_file_t *zlog_file_data, size_t len) +{ + char *cb = (char*)zlog_file_data->buffer; + char *buffer = (char*)zlog_file_data->buffer; + + endl_to_zero(zlog_file_data, len); + + while(cb < buffer + len ) { + + if(*cb == '\0' && cb < buffer + len ) { + cb++; + continue; + } + + int res = _zlog_write((const unsigned char)zlog_file_data->prio, (const char *)&zlog_file_data->tag, (const char *)cb); + if(res < 0) { + pr_err("_zlog_write failed\n"); + return -EFAULT; + } + + if(cb < buffer + len) { + cb += strnlen(cb, len - (cb - buffer)); + } + } + return 0; +} + +static ssize_t zlogger_write(struct file *filep, const char *buffer, size_t len, loff_t *offset) +{ + if( (!filep->private_data ) && init_file_zlog_tag_data(filep)) { + pr_err("zlogger_write init zlog tag data failed\n"); + return -ENOMEM; + } + + zlog_file_t *zlog_file_data = (zlog_file_t *)filep->private_data; + + if (len > zlog_file_data->buffer_len && !update_zlog_data_buffer_size(zlog_file_data, len)) { + pr_err("update_zlog_data_buffer_size failed\n"); + return -ENOMEM; + } + unsigned long copied = copy_from_user(zlog_file_data->buffer, buffer, len); + + if(copied != 0) { + pr_err("copy_from_user failed\n"); + return -EFAULT; + } + + ssize_t res = partition_write_buffer(zlog_file_data, len); + if(res < 0) { + pr_err("partition_write_buffer failed\n"); + return -EFAULT; + } + return len; +} + +static long update_prio(zlog_file_t *zlog_file_data, void __user *argp) +{ + char prio; + + prio = (char)(uintptr_t)argp; + + if (prio < (char)DLOG_DEFAULT || prio >=DLOG_PRIO_MAX) { + return -EINVAL; + } + zlog_file_data->prio = prio; + + return 0; +} + +static long zlogger_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + if (cmd == IOCTL_COMMAND_ALLOC) + return alloc_block_for_thread(false); + + #ifdef CONFIG_COMPAT + void __user *argp = compat_ptr(arg); + #else + void __user *argp = (void __user *)arg; + #endif + + if (cmd == SET_DEFAULT_PRIORITY) { + if( (!file->private_data ) && init_file_zlog_tag_data(file)) { + pr_err("zlogger_write init zlog tag data failed\n"); + return -ENOMEM; + } + + zlog_file_t *zlog_file_data = (zlog_file_t *)file->private_data; + + return update_prio(zlog_file_data, argp); + } + + if(cmd == SET_DEFAULT_TAG) { + if( (!file->private_data ) && init_file_zlog_tag_data(file)) { + pr_err("zlogger_write init zlog tag data failed\n"); + return -ENOMEM; + } + + zlog_file_t *zlog_file_data = (zlog_file_t *)file->private_data; + + return copy_from_user(&zlog_file_data->tag, (char *)(uintptr_t)argp, strnlen((char *)(uintptr_t)argp, MAX_TAG_SIZE)); + } + + return -EINVAL; +} + +static const struct file_operations zlogger_fops = { + .open = zlogger_open, + .read = zlogger_read, + .write = zlogger_write, + .release = zlogger_release, + .mmap = zlogger_mmap, + .unlocked_ioctl = zlogger_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = zlogger_ioctl, +#endif + .owner = THIS_MODULE, +}; + +static ssize_t status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int thread_count = 0; + int i; + int is_stdout; + + for (i = 0; i < g_max_thread_id; i++) { + for (is_stdout = 0; is_stdout <= 1; ++is_stdout) { + if (get_thread_table(i, is_stdout) > 0) + thread_count ++; + } + } + + return snprintf(buf, PAGE_SIZE, + "free(%d%%):%d/%d task_on:%d gc_free:%u error:%u tid_max:%d\n", + BLOCK_RATIO(g_free_q.count), g_free_q.count, (g_free_q.count + thread_count), + g_task_on, g_free_count, g_err_count, g_max_thread_id); +} + +static DEVICE_ATTR_RO(status); + +static ssize_t time_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, + "%llu %llu %llu\n", g_start_time, ktime_get_ns(), g_hwc_offset); +} + +static DEVICE_ATTR_RO(time); + +static uint16_t g_block = 1; + +static ssize_t block_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned long value; + + if (kstrtoul(buf, 10, &value) < 0) { + pr_err("Failed to get value"); + return -EINVAL; + } + + if (value < 1 || value > BLOCK_COUNT) + return -EINVAL; + + g_block = value; + + return count; +} + +static ssize_t block_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct block_t *block = get_block((uint16_t)g_block); + + if (!block) + return snprintf(buf, PAGE_SIZE, "[%d] Invalid block\n", g_block); + + return snprintf(buf, PAGE_SIZE, "[%d] pid:%u tid:%u offset:%u %llu %llu\n", + g_block, block->head.pid, block->head.tid, block->head.offset, + ((struct entry_t *)(block->data))->time, block->head.ts); +} + +static DEVICE_ATTR_RW(block); + +static uint16_t g_thread = 1; +static ssize_t thread_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + long value; + + if (kstrtol(buf, 10, &value) < 0) { + pr_err("Failed to get value"); + return -EINVAL; + } + + if (value < 1 || value > g_max_thread_id) + return -EINVAL; + + g_thread = value; + + return count; +} + +static ssize_t thread_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "[%d] block: %u, stdout block: %u\n", + g_thread, get_thread_table(g_thread, false), get_thread_table(g_thread, true)); +} + +static DEVICE_ATTR_RW(thread); + +static struct attribute *zlogger_attributes[] = { + &dev_attr_status.attr, + &dev_attr_time.attr, + &dev_attr_block.attr, + &dev_attr_thread.attr, + NULL, +}; + +static const struct attribute_group zlogger_attr_group = { + .attrs = zlogger_attributes, +}; + +int __init zlogger_init(void) +{ + int i = 0; + int g_shm_ptr_i = 0; + int r = 0; + + if (!g_zlog_enable) { + pr_info("zlog is disable\n"); + return 0; + } + + zlogger_device.minor = MISC_DYNAMIC_MINOR; + zlogger_device.name = ZLOGGER_DEVICE_NAME; + zlogger_device.fops = &zlogger_fops; + zlogger_device.mode = 0666; +#ifdef CONFIG_SECURITY_SMACK_SET_DEV_SMK_LABEL + zlogger_device.lab_smk64 = ZLOGGER_SMACK_LABEL; +#endif + r = misc_register(&zlogger_device); + if (unlikely(r)) { + pr_err("Failed to register misc device for '%s' (%d)\n", ZLOGGER_DEVICE_NAME, r); + return r; + } + + r = sysfs_create_group(&zlogger_device.this_device->kobj, &zlogger_attr_group); + if (unlikely(r)) { + dev_err(zlogger_device.this_device, "failed to create sysfs nodes with (%d)\n", r); + return r; + } + + g_thread_table = kzalloc(sizeof(struct thread_table), GFP_KERNEL); + if (g_thread_table == NULL) + return -ENOMEM; + hash_init(g_thread_table->data); + + for (g_shm_ptr_i = 0; g_shm_ptr_i < DEVICE_COUNT; g_shm_ptr_i++) { + g_shm_ptr[g_shm_ptr_i] = kzalloc(MAP_SIZE, GFP_KERNEL); + if (g_shm_ptr[g_shm_ptr_i] == NULL) { + r = -ENOMEM; + goto out_free_g_thread_table_g_shm_ptr; + } + } + + mutex_init(&g_block_mutex); + mutex_init(&g_task_mutex); + + init_completion(&g_completion); + + queue_init(&g_free_q, "free", BLOCK_COUNT); + for (i = 1; i <= BLOCK_COUNT; i++) + queue_push(&g_free_q, i); + +#ifdef CONFIG_TRACE_CLOCK + g_hwc_offset = trace_clock_local() - ktime_get_ns(); +#endif + run_task(); + + g_init = 1; + pr_info("Init success\n"); + + return 0; + +out_free_g_thread_table_g_shm_ptr: + for (i = 0; i < g_shm_ptr_i; ++i){ + kfree(g_shm_ptr[i]); + g_shm_ptr[i] = NULL; + } + + kfree(g_thread_table); + g_thread_table = NULL; + + return r; +} + +static void __exit zlogger_exit(void) +{ + int i; + struct thread_table_field *ptr = NULL; + struct hlist_node *tmp_iter = NULL; + int tmp_bkt; + + // TODO: What about the task that is running in the background? + + queue_deinit(&g_free_q); + hash_for_each_safe(g_thread_table->data, tmp_bkt, tmp_iter, ptr, next) { + kfree(ptr); + } + + kfree(g_thread_table); + g_thread_table = NULL; + + for (i = 0; i < DEVICE_COUNT; i++) { + kfree(g_shm_ptr[i]); + g_shm_ptr[i] = NULL; + } + + + + g_init = 0; + + sysfs_remove_group(&zlogger_device.this_device->kobj, &zlogger_attr_group); + misc_deregister(&zlogger_device); +} + +module_init(zlogger_init); +module_exit(zlogger_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("jh1009.sung , Arkadiusz Nowak , Mateusz Majewski +#include +#include +#include +#include +#include +#include +#include +#include + +// taken from dlog.h +typedef enum { + DLOG_UNKNOWN = 0, /**< Keep this always at the start */ + DLOG_DEFAULT, /**< Default */ + DLOG_VERBOSE, /**< Verbose */ + DLOG_DEBUG, /**< Debug */ + DLOG_INFO, /**< Info */ + DLOG_WARN, /**< Warning */ + DLOG_ERROR, /**< Error */ + DLOG_FATAL, /**< Fatal */ + DLOG_SILENT, /**< Silent */ + DLOG_PRIO_MAX /**< Keep this always at the end. */ +} log_priority; + +static const char* prio_default = "Prio: DEFAULT\n"; +static const char* prio_verbose = "Prio: VERBOSE\n"; +static const char* prio_debug = "Prio: DEBUG\n"; +static const char* prio_info = "Prio: INFO\n"; +static const char* prio_warn = "Prio: WARN\n"; +static const char* prio_error = "Prio: ERROR\n"; +static const char* prio_fatal = "Prio: FATAL\n"; +static const char* prio_silent = "Prio: SILENT\n"; + +static const char* test_message = "A test message\n"; + +int main(int argc, char *argv[]) +{ + printf("Simple zlog test!\n"); + printf("see at zlogutil messages\n"); + int fd = open("/dev/zlogger", O_RDWR); + if (fd < 0) { + printf("Error opening zlog device\n"); + return -1; + } + + ioctl(fd, SET_DEFAULT_PRIORITY, (char)DLOG_DEFAULT); + write(fd, prio_default, strlen(prio_default)); + + ioctl(fd, SET_DEFAULT_PRIORITY, (char)DLOG_VERBOSE); + write(fd, prio_verbose, strlen(prio_verbose)); + + ioctl(fd, SET_DEFAULT_PRIORITY, (char)DLOG_DEBUG); + write(fd, prio_debug, strlen(prio_debug)); + + ioctl(fd, SET_DEFAULT_PRIORITY, (char)DLOG_INFO); + write(fd, prio_info, strlen(prio_info)); + + ioctl(fd, SET_DEFAULT_PRIORITY, (char)DLOG_WARN); + write(fd, prio_warn, strlen(prio_warn)); + + ioctl(fd, SET_DEFAULT_PRIORITY, (char)DLOG_ERROR); + write(fd, prio_error, strlen(prio_error)); + + ioctl(fd, SET_DEFAULT_PRIORITY, (char)DLOG_FATAL); + write(fd, prio_fatal, strlen(prio_fatal)); + + ioctl(fd, SET_DEFAULT_PRIORITY, (char)DLOG_SILENT); + write(fd, prio_silent, strlen(prio_silent)); + + ioctl(fd, SET_DEFAULT_TAG, "TESTPROGRAM_1"); + write(fd, test_message, strlen(test_message)); + + ioctl(fd, SET_DEFAULT_PRIORITY, (char)DLOG_DEFAULT); + write(fd, prio_default, strlen(prio_default)); + + ioctl(fd, SET_DEFAULT_PRIORITY, (char)DLOG_VERBOSE); + write(fd, prio_verbose, strlen(prio_verbose)); + + ioctl(fd, SET_DEFAULT_PRIORITY, (char)DLOG_DEBUG); + write(fd, prio_debug, strlen(prio_debug)); + + ioctl(fd, SET_DEFAULT_PRIORITY, (char)DLOG_INFO); + write(fd, prio_info, strlen(prio_info)); + + ioctl(fd, SET_DEFAULT_PRIORITY, (char)DLOG_WARN); + write(fd, prio_warn, strlen(prio_warn)); + + ioctl(fd, SET_DEFAULT_PRIORITY, (char)DLOG_ERROR); + write(fd, prio_error, strlen(prio_error)); + + ioctl(fd, SET_DEFAULT_PRIORITY, (char)DLOG_FATAL); + write(fd, prio_fatal, strlen(prio_fatal)); + + ioctl(fd, SET_DEFAULT_PRIORITY, (char)DLOG_SILENT); + write(fd, prio_silent, strlen(prio_silent)); + + ioctl(fd, SET_DEFAULT_TAG, "TESTPROGRAM_2"); + write(fd, test_message, strlen(test_message)); + return 0; +}