From: Arkadiusz Nowak Date: Tue, 21 Jun 2022 11:14:50 +0000 (+0200) Subject: Included tizen tv logging kernel module X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=refs%2Fchanges%2F56%2F279256%2F7;p=platform%2Fkernel%2Flinux-tizen-modules-source.git Included tizen tv logging kernel module Added write function via file interface Additional ioctl's commands to set tag and prio simple test like vlog_stdout program to check tag and prio change Change-Id: Ia04f1add0a4904bfd92b156022c704bc62495931 --- diff --git a/include/uapi/linux/vlogger.h b/include/uapi/linux/vlogger.h new file mode 100644 index 0000000..81dd613 --- /dev/null +++ b/include/uapi/linux/vlogger.h @@ -0,0 +1,8 @@ +#ifndef _VLOGGER_H +#define _VLOGGER_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..2830563 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_vlog ?= n obj-$(BUILD_logger) += logger.o obj-$(BUILD_proc_tsm) += proc-tsm.o obj-$(BUILD_kdbus) += kdbus/ +obj-$(BUILD_vlog) += vlogger/ all: make -C $(KERNELDIR) M=$(PWD) CFLAGS_MODULE=-I$(PWD)/include modules diff --git a/kernel/vlogger/Makefile b/kernel/vlogger/Makefile new file mode 100644 index 0000000..c213013 --- /dev/null +++ b/kernel/vlogger/Makefile @@ -0,0 +1,3 @@ +ccflags-y += -std=c11 -std=gnu11 -Wno-error=missing-attributes -Wno-error=declaration-after-statement +vlog-y := vlogger.o +obj-m += vlog.o diff --git a/kernel/vlogger/vlogger.c b/kernel/vlogger/vlogger.c new file mode 100644 index 0000000..2546979 --- /dev/null +++ b/kernel/vlogger/vlogger.c @@ -0,0 +1,810 @@ +/* + * drivers/misc/vlogger.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) "vlogger: " fmt + +#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)) +// TODO: We do not want to have a MAX_THREAD limitation. Remove this. +#define MAX_THREAD (0x8000) +#define MS_PER_SEC (1000) +#define NS_PER_SEC (1000000000UL) + +#define VLOGGER_DEVICE_NAME "vlog" +#define VLOGGER_SMACK_LABEL "*" + +#define MAX_TAG_SIZE (32) +#define MAX_MSG_SIZE (140) + +#define BLOCK_RATIO(count) (count*100/BLOCK_COUNT) + +#define VLOG_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; +}; + +// 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; + +/*vlogger 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; +} vlog_file_t; + +/*--vlogger file channel*/ + +static struct miscdevice vlogger_device; + +static int g_init; +static char *g_shm_ptr[DEVICE_COUNT]; +// TODO: Having an array indexed by TIDs is unnacceptable. +static struct thread_t *g_threads; + +// TODO: g_block_mutex is basically smoke and mirrors, +// since the protected array is basically world-writable +// and can change at any time. +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_vlog_enable = 1; +module_param_named(vlog_enable, g_vlog_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 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) +{ + // TODO: This represents the same 2048 value in three different ways: + // 1 << 11, 0x7FF + 1, as well as V_BLOCK_SIZE. + uint16_t index = block_index - 1; + int offset = index & 0x7FF; + char *p = get_shared_memory(index >> 11); + + if (!p) { + pr_err("[INVALID BLOCK] index:%d free:%d err:%d", block_index, g_free_q.count, g_err_count); + dump_stack(); + WARN_ON(1); + } + + return (struct block_t *)(p + (offset << 11)); +} + +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 vlog_task(void *user_data) +{ + int i; + uint16_t blk; + + do { + for (i = 1; i <= g_max_thread_id; i++) { + blk = g_threads[i].block; + // 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); + queue_push(&g_free_q, blk); + g_threads[i].block = 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(vlog_task, NULL, "vlog_task"); + if (IS_ERR(task)) { + pr_err("Failed to run vlog_task\n"); + g_task_on = 0; + } +} + +static long alloc_block_for_thread(void) +{ + pid_t pid = current->tgid; + pid_t tid = current->pid; + uint16_t blk; + struct block_t *block; + + if (tid >= MAX_THREAD) { + pr_err("Invalid tid: %d", tid); + return -EINVAL; + } + + if (g_max_thread_id < tid) + g_max_thread_id = tid; + + mutex_lock(&g_block_mutex); + blk = g_threads[tid].block; + if (blk) + queue_push(&g_free_q, blk); + blk = queue_pop(&g_free_q); + g_threads[tid].block = blk; + mutex_unlock(&g_block_mutex); + + 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); + 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; + + return (long)blk; +} + +static inline struct block_t *get_valid_block(int tid, size_t len) +{ + uint16_t blk = 0; + long r; + + if (g_threads == NULL) + return NULL; + + blk = g_threads[tid].block; + + 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(); + if (r <= 0) + return NULL; + + return get_block((uint16_t)r); +} + +static int vlogger_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 vlogger_release(struct inode *ignored, struct file *file) +{ + if (file->private_data != NULL) { + vlog_file_t *vlog_file_data = (vlog_file_t *)file->private_data; + + if (vlog_file_data->buffer != NULL) { + kfree(vlog_file_data->buffer); + vlog_file_data->buffer = NULL; + } + kfree(file->private_data); + file->private_data = NULL; + } + return 0; +} + +static int vlogger_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 + // TODO: There is no reason for people to be able to write to g_threads. + page = virt_to_page((unsigned long)g_threads); + + return remap_pfn_range(vma, vma->vm_start, page_to_pfn(page), size, vma->vm_page_prot); +} + +static ssize_t vlogger_read(struct file *filep, char *buffer, size_t len, loff_t *offset) +{ + pr_err("read failed!\n"); + return -EPERM; +} + +static ssize_t init_file_vlog_tag_data(struct file *filep) +{ + vlog_file_t *vlog_file_data = kmalloc(sizeof(vlog_file_t), GFP_KERNEL); + if (!vlog_file_data) { + pr_err("init_vlog_tag_data: no memory \n"); + return -ENOMEM; + } + vlog_file_data->buffer = NULL; + memcpy(vlog_file_data->tag, default_tag, sizeof(default_tag) - 1); + vlog_file_data->tag[sizeof(default_tag) - 1] = '\0'; + vlog_file_data->prio = (char)DLOG_INFO; + vlog_file_data->buffer = kmalloc(LOG_BUFFER_SIZE, GFP_KERNEL); + + if (!vlog_file_data->buffer) { + kfree(vlog_file_data); + return -ENOMEM; + } + vlog_file_data->buffer_len = LOG_BUFFER_SIZE; + filep->private_data = (void *)vlog_file_data; + return 0; +} + +int update_vlog_data_buffer_size(vlog_file_t *vlog_file_data, size_t len) +{ + char *new_buffer = kmalloc(len, GFP_KERNEL); + if (!new_buffer) + return -ENOMEM; + kfree(vlog_file_data->buffer); + vlog_file_data->buffer = new_buffer; + vlog_file_data->buffer_len = len; + return 0; +} + +static int _vlog_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); + struct entry_t *entry; + struct entry_t tmp; + + if (block == NULL) { + pr_err("_vlog_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(vlog_file_t *vlog_file_data, size_t len) +{ + char *cb = (char*)vlog_file_data->buffer; + while(cb < (char*)vlog_file_data->buffer + len) { + if(*cb == '\n') { + *cb = '\0'; + } + cb++; + } +} + +static ssize_t partition_write_buffer(vlog_file_t *vlog_file_data, size_t len) +{ + char *cb = (char*)vlog_file_data->buffer; + char *buffer = (char*)vlog_file_data->buffer; + + endl_to_zero(vlog_file_data, len); + + while(cb < buffer + len ) { + + if(*cb == '\0' && cb < buffer + len ) { + cb++; + continue; + } + + int res = _vlog_write((const unsigned char)vlog_file_data->prio, (const char *)&vlog_file_data->tag, (const char *)cb); + if(res < 0) { + pr_err("_vlog_write failed\n"); + return -EFAULT; + } + + if(cb < buffer + len) { + cb += strnlen(cb, len - (cb - buffer)); + } + } + return 0; +} + +static ssize_t vlogger_write(struct file *filep, const char *buffer, size_t len, loff_t *offset) +{ + if( (!filep->private_data ) && init_file_vlog_tag_data(filep)) { + pr_err("vlogger_write init vlog tag data failed\n"); + return -ENOMEM; + } + + vlog_file_t *vlog_file_data = (vlog_file_t *)filep->private_data; + + if (len > vlog_file_data->buffer_len && !update_vlog_data_buffer_size(vlog_file_data, len)) { + pr_err("update_vlog_data_buffer_size failed\n"); + return -ENOMEM; + } + unsigned long copied = copy_from_user(vlog_file_data->buffer, buffer, len); + + if(copied != 0) { + pr_err("copy_from_user failed\n"); + return -EFAULT; + } + + ssize_t res = partition_write_buffer(vlog_file_data, len); + if(res < 0) { + pr_err("partition_write_buffer failed\n"); + return -EFAULT; + } + return len; +} + +static long update_prio(vlog_file_t *vlog_file_data, void __user *argp) +{ + char prio; + + prio = (char)(uintptr_t)argp; + + if (prio < (char)DLOG_DEFAULT || prio >=DLOG_PRIO_MAX) { + return -EINVAL; + } + vlog_file_data->prio = prio; + + return 0; +} + +static long vlogger_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + if (cmd == IOCTL_COMMAND_ALLOC) + return alloc_block_for_thread(); + + + void __user *argp = (void __user *)arg; + + if (cmd == SET_DEFAULT_PRIORITY) { + if( (!file->private_data ) && init_file_vlog_tag_data(file)) { + pr_err("vlogger_write init vlog tag data failed\n"); + return -ENOMEM; + } + + vlog_file_t *vlog_file_data = (vlog_file_t *)file->private_data; + + return update_prio(vlog_file_data, argp); + } + + if(cmd == SET_DEFAULT_TAG) { + if( (!file->private_data ) && init_file_vlog_tag_data(file)) { + pr_err("vlogger_write init vlog tag data failed\n"); + return -ENOMEM; + } + + vlog_file_t *vlog_file_data = (vlog_file_t *)file->private_data; + + return copy_from_user(&vlog_file_data->tag, (char *)(uintptr_t)argp, strnlen((char *)(uintptr_t)argp, MAX_TAG_SIZE)); + } + + return -EINVAL; +} + +static const struct file_operations vlogger_fops = { + .open = vlogger_open, + .read = vlogger_read, + .write = vlogger_write, + .release = vlogger_release, + .mmap = vlogger_mmap, + .unlocked_ioctl = vlogger_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = vlogger_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; + + for (i = 0; i < MAX_THREAD; i++) { + if (g_threads[i].block > 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\n", g_thread, g_threads[g_thread].block); +} + +static DEVICE_ATTR_RW(thread); + +static struct attribute *vlogger_attributes[] = { + &dev_attr_status.attr, + &dev_attr_time.attr, + &dev_attr_block.attr, + &dev_attr_thread.attr, + NULL, +}; + +static const struct attribute_group vlogger_attr_group = { + .attrs = vlogger_attributes, +}; + +int __init vlogger_init(void) +{ + int i = 0; + int g_shm_ptr_i = 0; + int r = 0; + + if (!g_vlog_enable) { + pr_info("vlog is disable\n"); + return 0; + } + + vlogger_device.minor = MISC_DYNAMIC_MINOR; + vlogger_device.name = VLOGGER_DEVICE_NAME; + vlogger_device.fops = &vlogger_fops; + vlogger_device.mode = 0666; +#ifdef CONFIG_SECURITY_SMACK_SET_DEV_SMK_LABEL + vlogger_device.lab_smk64 = VLOGGER_SMACK_LABEL; +#endif + r = misc_register(&vlogger_device); + if (unlikely(r)) { + pr_err("Failed to register misc device for '%s' (%d)\n", VLOGGER_DEVICE_NAME, r); + return r; + } + + r = sysfs_create_group(&vlogger_device.this_device->kobj, &vlogger_attr_group); + if (unlikely(r)) { + dev_err(vlogger_device.this_device, "failed to create sysfs nodes with (%d)\n", r); + return r; + } + + g_threads = kzalloc(sizeof(struct thread_t) * MAX_THREAD, GFP_KERNEL); + if (g_threads == NULL) + return -ENOMEM; + + 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_threads_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_threads_g_shm_ptr: + for (i = 0; i < g_shm_ptr_i; ++i) + kfree(g_shm_ptr[i]); + kfree(g_threads); + return r; +} + +static void __exit vlogger_exit(void) +{ + int i; + + // TODO: What about the task that is running in the background? + + queue_deinit(&g_free_q); + kfree(g_threads); + for (i = 0; i < DEVICE_COUNT; i++) + kfree(g_shm_ptr[i]); + + sysfs_remove_group(&vlogger_device.this_device->kobj, &vlogger_attr_group); + misc_deregister(&vlogger_device); +} + + +module_init(vlogger_init); +module_exit(vlogger_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("jh1009.sung, , Arkadiusz Nowak "); +MODULE_DESCRIPTION("Tizen Vlogger"); diff --git a/packaging/linux-tizen-modules-source.spec b/packaging/linux-tizen-modules-source.spec index b7e4cb2..5c85254 100644 --- a/packaging/linux-tizen-modules-source.spec +++ b/packaging/linux-tizen-modules-source.spec @@ -35,21 +35,26 @@ cp %{SOURCE1} . %build make -C tests/kdbus make -C tests/logger +make -C tests/vlog_stdout %install mkdir -p %{buildroot}/usr/src/%{name}/kdbus +mkdir -p %{buildroot}/usr/src/%{name}/vlogger mkdir -p %{buildroot}/%{_includedir}/linux mkdir -p %{buildroot}/%{_libexecdir}/%{name}/tests cp -a include/ %{buildroot}/usr/src/%{name} cp kernel/*.[ch] kernel/Makefile COPYING %{buildroot}/usr/src/%{name} cp kernel/kdbus/*.[ch] kernel/kdbus/Makefile %{buildroot}/usr/src/%{name}/kdbus +cp kernel/vlogger/*.c kernel/vlogger/Makefile %{buildroot}/usr/src/%{name}/vlogger cp include/uapi/linux/kdbus.h %{buildroot}/%{_includedir}/linux cp include/uapi/linux/logger.h %{buildroot}/%{_includedir}/linux +cp include/uapi/linux/vlogger.h %{buildroot}/%{_includedir}/linux cp tests/logger/logger-test %{buildroot}/%{_libexecdir}/%{name}/tests/logger-test cp tests/kdbus/kdbus-test %{buildroot}/%{_libexecdir}/%{name}/tests/kdbus-test +cp tests/vlog_stdout/vlog_stdout %{buildroot}/%{_libexecdir}/%{name}/tests/vlog_stdout %files %manifest %{name}.manifest @@ -62,14 +67,19 @@ cp tests/kdbus/kdbus-test %{buildroot}/%{_libexecdir}/%{name}/tests/kdbus-test /usr/src/%{name}/kdbus/Makefile /usr/src/%{name}/include/uapi/linux/*.h +/usr/src/%{name}/vlogger/*.c +/usr/src/%{name}/vlogger/Makefile + %files -n linux-tizen-modules-headers %manifest %{name}.manifest %license COPYING %{_includedir}/linux/kdbus.h %{_includedir}/linux/logger.h +%{_includedir}/linux/vlogger.h %files -n linux-tizen-modules-tests %manifest %{name}.manifest %license COPYING %{_libexecdir}/%{name}/tests/logger-test %{_libexecdir}/%{name}/tests/kdbus-test +%{_libexecdir}/%{name}/tests/vlog_stdout diff --git a/tests/vlog_stdout/Makefile b/tests/vlog_stdout/Makefile new file mode 100644 index 0000000..785eb03 --- /dev/null +++ b/tests/vlog_stdout/Makefile @@ -0,0 +1,25 @@ +CC := $(CROSS_COMPILE)gcc +CFLAGS += -I../../include/uapi +CFLAGS += -std=gnu11 +CFLAGS += -D_GNU_SOURCE +CFLAGS += -Wno-error=unused-result +LDFLAGS = -pthread + +.PHONY: all clean + +TEST_CUSTOM_PROGS := vlog_stdout +all: $(TEST_CUSTOM_PROGS) + +OBJS = \ + vlog_stdout.o + +$(TEST_CUSTOM_PROGS): $(OBJS) + $(CC) -o $(TEST_CUSTOM_PROGS) $(OBJS) $(LDFLAGS) + +$(OBJS): %.o: %.c + $(CC) -c $^ -o $@ $(CFLAGS) + +EXTRA_CLEAN := $(TEST_CUSTOM_PROGS) $(OBJS) + +clean: + $(RM) -r $(TEST_GEN_PROGS) $(TEST_GEN_PROGS_EXTENDED) $(TEST_GEN_FILES) $(EXTRA_CLEAN) diff --git a/tests/vlog_stdout/vlog_stdout.c b/tests/vlog_stdout/vlog_stdout.c new file mode 100644 index 0000000..090b2ad --- /dev/null +++ b/tests/vlog_stdout/vlog_stdout.c @@ -0,0 +1,100 @@ +#include +#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 vlog test!\n"); + printf("see at vlogutil messages\n"); + int fd = open("/dev/vlog", O_RDWR); + if (fd < 0) { + printf("Error opening vlog 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; +}