diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index f9aef39cac2e93dbb488b62f44236c49eecbf338..5dc634897b4843dab8d6f92c53fb2ccb0e191255 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -78,4 +78,13 @@ source "drivers/staging/qlge/Kconfig" source "drivers/staging/vme_user/Kconfig" +source "drivers/staging/hilog/Kconfig" + +source "drivers/staging/hievent/Kconfig" + +source "drivers/staging/hisysevent/Kconfig" + +source "drivers/staging/zerohung/Kconfig" + +source "drivers/staging/hungtask/Kconfig" endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index ffa70dda481d36c526a11275a2b7896957a4edc3..aec6e94a3c69048caaac17039bca3690c7819784 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -28,3 +28,8 @@ obj-$(CONFIG_PI433) += pi433/ obj-$(CONFIG_XIL_AXIS_FIFO) += axis-fifo/ obj-$(CONFIG_FIELDBUS_DEV) += fieldbus/ obj-$(CONFIG_QLGE) += qlge/ +obj-$(CONFIG_HILOG) += hilog/ +obj-$(CONFIG_HIEVENT) += hievent/ +obj-$(CONFIG_HISYSEVENT) += hisysevent/ +obj-$(CONFIG_DFX_ZEROHUNG) += zerohung/ +obj-$(CONFIG_DFX_HUNGTASK) += hungtask/ diff --git a/drivers/staging/hievent/Kconfig b/drivers/staging/hievent/Kconfig new file mode 100644 index 0000000000000000000000000000000000000000..b445a2b90a0745f91d10d6d6c1ee5c0e811b7c5c --- /dev/null +++ b/drivers/staging/hievent/Kconfig @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0 +config HIEVENT + tristate "Enable hievent" + help + hievent buffer manager + +config BBOX_BUFFER_SIZE + int "bbox buffer size" + depends on HIEVENT + default 2048 + help + Define the default ring buffer size of BBOX \ No newline at end of file diff --git a/drivers/staging/hievent/Makefile b/drivers/staging/hievent/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..5b2adc23affc87a4d187c2372da6327cd54a5a2d --- /dev/null +++ b/drivers/staging/hievent/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_HIEVENT) += hievent_driver.o \ No newline at end of file diff --git a/drivers/staging/hievent/hievent_driver.c b/drivers/staging/hievent/hievent_driver.c new file mode 100644 index 0000000000000000000000000000000000000000..36fa7eb06b090d2d8e29e90f9bcfb1e4f9687e55 --- /dev/null +++ b/drivers/staging/hievent/hievent_driver.c @@ -0,0 +1,422 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 Huawei Technologies Co., Ltd. All rights reserved. + */ + +#define pr_fmt(fmt) "hievent_driver " fmt + +#include "hievent_driver.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct class *hievent_class; +static dev_t hievent_devno; + +#define HIEVENT_BUFFER ((size_t)CONFIG_BBOX_BUFFER_SIZE) +#define HIEVENT_DRIVER "/dev/bbox" +#define HIEVENT_DEV_NAME "bbox" +#define HIEVENT_DEV_NR 1 + +struct hievent_entry { + unsigned short len; + unsigned short header_size; + char msg[0]; +}; + +struct hievent_char_device { + struct cdev devm; + int flag; + struct mutex mtx; /* lock to protect read/write buffer */ + unsigned char *buffer; + wait_queue_head_t wq; + size_t write_offset; + size_t head_offset; + size_t size; + size_t count; +} hievent_dev; + +static inline unsigned char *hievent_buffer_head(void) +{ + if (hievent_dev.head_offset > HIEVENT_BUFFER) + hievent_dev.head_offset = + hievent_dev.head_offset % HIEVENT_BUFFER; + + return hievent_dev.buffer + hievent_dev.head_offset; +} + +static void hievent_buffer_inc(size_t sz) +{ + if (hievent_dev.size + sz <= HIEVENT_BUFFER) { + hievent_dev.size += sz; + hievent_dev.write_offset += sz; + hievent_dev.write_offset %= HIEVENT_BUFFER; + hievent_dev.count++; + } +} + +static void hievent_buffer_dec(size_t sz) +{ + if (hievent_dev.size >= sz) { + hievent_dev.size -= sz; + hievent_dev.head_offset += sz; + hievent_dev.head_offset %= HIEVENT_BUFFER; + hievent_dev.count--; + } +} + +static int hievent_read_ring_buffer(unsigned char __user *buffer, + size_t buf_len) +{ + size_t retval; + size_t buf_left = HIEVENT_BUFFER - hievent_dev.head_offset; + + if (buf_left > buf_len) { + retval = copy_to_user(buffer, hievent_buffer_head(), buf_len); + } else { + size_t mem_len = (buf_len > buf_left) ? buf_left : buf_len; + + retval = copy_to_user(buffer, hievent_buffer_head(), mem_len); + if (retval < 0) + return retval; + + retval = copy_to_user(buffer + buf_left, hievent_dev.buffer, + buf_len - buf_left); + } + return retval; +} + +static int hievent_read_ring_head_buffer(unsigned char * const buffer, + size_t buf_len) +{ + size_t buf_left = HIEVENT_BUFFER - hievent_dev.head_offset; + + if (buf_left > buf_len) { + memcpy(buffer, hievent_buffer_head(), buf_len); + } else { + size_t mem_len = (buf_len > buf_left) ? buf_left : buf_len; + + memcpy(buffer, hievent_buffer_head(), mem_len); + memcpy(buffer + buf_left, hievent_dev.buffer, + buf_len - buf_left); + } + return 0; +} + +static ssize_t hievent_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + size_t retval; + struct hievent_entry header; + + (void)file; + + if (wait_event_interruptible(hievent_dev.wq, (hievent_dev.size > 0))) + return -EINVAL; + + (void)mutex_lock(&hievent_dev.mtx); + + if (hievent_dev.size == 0) { + retval = 0; + goto out; + } + + retval = hievent_read_ring_head_buffer((unsigned char *)&header, + sizeof(header)); + if (retval < 0) { + retval = -EINVAL; + goto out; + } + + if (count < header.len + sizeof(header)) { + retval = -ENOMEM; + goto out; + } + + hievent_buffer_dec(sizeof(header)); + + retval = hievent_read_ring_buffer((unsigned char __user *)(user_buf), header.len); + if (retval < 0) { + retval = -EINVAL; + goto out; + } + hievent_buffer_dec(header.len); + + retval = header.len + sizeof(header); +out: + if (retval == -ENOMEM) { + // clean ring buffer + hievent_dev.write_offset = 0; + hievent_dev.head_offset = 0; + hievent_dev.size = 0; + hievent_dev.count = 0; + } + (void)mutex_unlock(&hievent_dev.mtx); + + return retval; +} + +static int hievent_write_ring_head_buffer(const unsigned char *buffer, + size_t buf_len) +{ + size_t buf_left = HIEVENT_BUFFER - hievent_dev.write_offset; + + if (buf_len > buf_left) { + memcpy(hievent_dev.buffer + hievent_dev.write_offset, + buffer, buf_left); + memcpy(hievent_dev.buffer, buffer + buf_left, + min(HIEVENT_BUFFER, buf_len - buf_left)); + } else { + memcpy(hievent_dev.buffer + hievent_dev.write_offset, + buffer, min(buf_left, buf_len)); + } + + return 0; +} + +static void hievent_head_init(struct hievent_entry * const header, size_t len) +{ + header->len = (unsigned short)len; + header->header_size = sizeof(struct hievent_entry); +} + +static void hievent_cover_old_log(size_t buf_len) +{ + int retval; + struct hievent_entry header; + size_t total_size = buf_len + sizeof(struct hievent_entry); + + while (total_size + hievent_dev.size > HIEVENT_BUFFER) { + retval = hievent_read_ring_head_buffer((unsigned char *)&header, + sizeof(header)); + if (retval < 0) + break; + + /* let count decrease twice */ + hievent_buffer_dec(sizeof(header)); + hievent_buffer_dec(header.len); + } +} + +int hievent_write_internal(const char *buffer, size_t buf_len) +{ + struct hievent_entry header; + int retval; + + if (buf_len < sizeof(int) || + buf_len > HIEVENT_BUFFER - sizeof(struct hievent_entry)) + return -EINVAL; + + (void)mutex_lock(&hievent_dev.mtx); + + hievent_cover_old_log(buf_len); + + hievent_head_init(&header, buf_len); + retval = hievent_write_ring_head_buffer((unsigned char *)&header, + sizeof(header)); + if (retval) { + retval = -EINVAL; + goto out; + } + hievent_buffer_inc(sizeof(header)); + + retval = hievent_write_ring_head_buffer((unsigned char *)(buffer), + header.len); + if (retval) { + retval = -EINVAL; + goto out; + } + + hievent_buffer_inc(header.len); + + retval = header.len; + +out: + (void)mutex_unlock(&hievent_dev.mtx); + if (retval > 0) + wake_up_interruptible(&hievent_dev.wq); + + return retval; +} + +static unsigned int hievent_poll(struct file *filep, poll_table *wait) +{ + unsigned int mask = 0; + + poll_wait(filep, &hievent_dev.wq, wait); + if (hievent_dev.size > 0) { + mask |= POLLIN | POLLRDNORM; + return mask; + } + + return 0; +} + +static ssize_t hievent_write_iter(struct kiocb *iocb, struct iov_iter *from) +{ + int check_code = 0; + unsigned char *temp_buffer = NULL; + const struct iovec *iov = iter_iov(from); + int retval; + size_t buf_len; + (void)iocb; + + if (from->nr_segs != 2) { /* must contain 2 segments */ + pr_err("invalid nr_segs: %ld", from->nr_segs); + retval = -EINVAL; + goto out; + } + + /* seg 0 info is checkcode*/ + retval = copy_from_user(&check_code, iov[0].iov_base, + sizeof(check_code)); + if (retval || check_code != CHECK_CODE) { + retval = -EINVAL; + goto out; + } + + /* seg 1 info */ + buf_len = iov[1].iov_len; + if (buf_len > HIEVENT_BUFFER - sizeof(struct hievent_entry)) { + retval = -ENOMEM; + goto out; + } + + temp_buffer = kmalloc(buf_len, GFP_KERNEL); + if (!temp_buffer) { + retval = -ENOMEM; + goto out; + } + + retval = copy_from_user(temp_buffer, iov[1].iov_base, iov[1].iov_len); + if (retval) { + retval = -EIO; + goto free_mem; + } + + retval = hievent_write_internal(temp_buffer, buf_len); + if (retval < 0) { + retval = -EIO; + goto free_mem; + } + retval = buf_len + iov[0].iov_len; + +free_mem: + kfree(temp_buffer); + +out: + return retval; +} + +static const struct file_operations hievent_fops = { + .read = hievent_read, /* read */ + .poll = hievent_poll, /* poll */ + .write_iter = hievent_write_iter, /* write_iter */ +}; + +static int hievent_device_init(void) +{ + hievent_dev.buffer = kmalloc(HIEVENT_BUFFER, GFP_KERNEL); + if (!hievent_dev.buffer) + return -ENOMEM; + + init_waitqueue_head(&hievent_dev.wq); + mutex_init(&hievent_dev.mtx); + hievent_dev.write_offset = 0; + hievent_dev.head_offset = 0; + hievent_dev.size = 0; + hievent_dev.count = 0; + + return 0; +} + +static int __init hieventdev_init(void) +{ + int result; + struct device *dev_ret = NULL; + + result = alloc_chrdev_region(&hievent_devno, 0, HIEVENT_DEV_NR, HIEVENT_DEV_NAME); + if (result < 0) { + pr_err("register %s failed", HIEVENT_DRIVER); + return -ENODEV; + } + + cdev_init(&hievent_dev.devm, &hievent_fops); + hievent_dev.devm.owner = THIS_MODULE; + + result = cdev_add(&hievent_dev.devm, hievent_devno, HIEVENT_DEV_NR); + if (result < 0) { + pr_err("cdev_add failed"); + goto unreg_dev; + } + + result = hievent_device_init(); + if (result < 0) { + pr_err("hievent_device_init failed"); + goto del_dev; + } + + hievent_class = class_create(HIEVENT_DEV_NAME); + if (IS_ERR(hievent_class)) { + pr_err("class_create failed"); + goto del_buffer; + } + + dev_ret = device_create(hievent_class, 0, hievent_devno, 0, HIEVENT_DEV_NAME); + if (IS_ERR(dev_ret)) { + pr_err("device_create failed"); + goto del_class; + } + + return 0; + +del_class: + class_destroy(hievent_class); +del_buffer: + kfree(hievent_dev.buffer); +del_dev: + cdev_del(&hievent_dev.devm); +unreg_dev: + unregister_chrdev_region(hievent_devno, HIEVENT_DEV_NR); + + return -ENODEV; +} + +static void __exit hievent_exit_module(void) +{ + device_destroy(hievent_class, hievent_devno); + class_destroy(hievent_class); + kfree(hievent_dev.buffer); + cdev_del(&hievent_dev.devm); + unregister_chrdev_region(hievent_devno, HIEVENT_DEV_NR); +} + +static int __init hievent_init_module(void) +{ + int state; + + state = hieventdev_init(); + return 0; +} + +module_init(hievent_init_module); +module_exit(hievent_exit_module); + +MODULE_AUTHOR("OHOS"); +MODULE_DESCRIPTION("User mode hievent device interface"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("hievent"); diff --git a/drivers/staging/hievent/hievent_driver.h b/drivers/staging/hievent/hievent_driver.h new file mode 100644 index 0000000000000000000000000000000000000000..83c67d9d2e152723c39659dd40b16c4b3a247983 --- /dev/null +++ b/drivers/staging/hievent/hievent_driver.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2021 Huawei Technologies Co., Ltd. All rights reserved. + */ + +#ifndef HIEVENT_DRIVER_H +#define HIEVENT_DRIVER_H + +#include + +#define CHECK_CODE 0x7BCDABCD + +struct idap_header { + char level; + char category; + char log_type; + char sn; +}; + +int hievent_write_internal(const char *buffer, size_t buf_len); + +#endif /* HIEVENT_DRIVER_H */ diff --git a/drivers/staging/hievent/hiview_hievent.c b/drivers/staging/hievent/hiview_hievent.c new file mode 100644 index 0000000000000000000000000000000000000000..4533b6fbb7595036952b003ce83a31afff7cb595 --- /dev/null +++ b/drivers/staging/hievent/hiview_hievent.c @@ -0,0 +1,488 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 Huawei Technologies Co., Ltd. All rights reserved. + */ + +#include "hiview_hievent.h" +#include "hievent_driver.h" + +#include +#include + +#define INT_TYPE_MAX_LEN 21 + +#define MAX_PATH_LEN 256 +#define MAX_STR_LEN (10 * 1024) + +/* CONFIG_BBOX_BUFFER_SIZE is max length of /dev/bbox */ +#define EVENT_INFO_BUF_LEN ((size_t)CONFIG_BBOX_BUFFER_SIZE) +#define EVENT_INFO_PACK_BUF_LEN min((size_t)CONFIG_BBOX_BUFFER_SIZE, 2048) + +#define BUF_POINTER_FORWARD \ +do { \ + if (tmplen < len) { \ + tmp += tmplen; \ + len -= tmplen; \ + } else { \ + tmp += len; \ + len = 0; \ + } \ +} while (0) + +struct hievent_payload { + char *key; + char *value; + struct hievent_payload *next; +}; + +static int hievent_convert_string(struct hiview_hievent *event, char **pbuf); + +static struct hievent_payload *hievent_payload_create(void); + +static void hievent_payload_destroy(struct hievent_payload *p); + +static struct hievent_payload *hievent_get_payload(struct hievent_payload *head, + const char *key); + +static void hievent_add_payload(struct hiview_hievent *obj, + struct hievent_payload *payload); + +static struct hievent_payload *hievent_payload_create(void) +{ + struct hievent_payload *payload = NULL; + + payload = kmalloc(sizeof(*payload), GFP_KERNEL); + if (!payload) + return NULL; + + payload->key = NULL; + payload->value = NULL; + payload->next = NULL; + + return payload; +} + +static void hievent_payload_destroy(struct hievent_payload *p) +{ + if (!p) + return; + + kfree(p->value); + kfree(p->key); + kfree(p); +} + +static struct hievent_payload *hievent_get_payload(struct hievent_payload *head, + const char *key) +{ + struct hievent_payload *p = head; + + while (p) { + if (key && p->key) { + if (strcmp(p->key, key) == 0) + return p; + } + p = p->next; + } + + return NULL; +} + +static void hievent_add_payload(struct hiview_hievent *obj, + struct hievent_payload *payload) +{ + if (!obj->head) { + obj->head = payload; + } else { + struct hievent_payload *p = obj->head; + + while (p->next) + p = p->next; + p->next = payload; + } +} + +struct hiview_hievent *hievent_create(unsigned int eventid) +{ + struct hiview_hievent *event = NULL; + + /* combined event obj struct */ + event = kmalloc(sizeof(*event), GFP_KERNEL); + if (!event) + return NULL; + + memset(event, 0, sizeof(*event)); + event->eventid = eventid; + pr_debug("%s : %u\n", __func__, eventid); + + return (void *)event; +} + +int hievent_put_integer(struct hiview_hievent *event, + const char *key, long value) +{ + int ret; + struct hievent_payload *payload = NULL; + + if ((!event) || (!key)) { + pr_err("Bad input event or key for %s", __func__); + return -EINVAL; + } + + payload = hievent_get_payload(event->head, key); + if (!payload) { + payload = hievent_payload_create(); + if (!payload) + return -ENOMEM; + payload->key = kstrdup(key, GFP_KERNEL); + hievent_add_payload(event, payload); + } + + kfree(payload->value); + + payload->value = kmalloc(INT_TYPE_MAX_LEN, GFP_KERNEL); + if (!payload->value) + return -ENOMEM; + + (void)memset(payload->value, 0, INT_TYPE_MAX_LEN); + ret = snprintf(payload->value, INT_TYPE_MAX_LEN, "%d", (int)value); + if (ret < 0) + return -ENOMEM; + + return 0; +} + +int hievent_put_string(struct hiview_hievent *event, + const char *key, const char *value) +{ + struct hievent_payload *payload = NULL; + int len; + + if ((!event) || (!key) || (!value)) { + pr_err("Bad key for %s", __func__); + return -EINVAL; + } + + payload = hievent_get_payload(event->head, key); + if (!payload) { + payload = hievent_payload_create(); + if (!payload) + return -ENOMEM; + + payload->key = kstrdup(key, GFP_KERNEL); + hievent_add_payload(event, payload); + } + + kfree(payload->value); + + len = strlen(value); + /* prevent length larger than MAX_STR_LEN */ + if (len > MAX_STR_LEN) + len = MAX_STR_LEN; + + payload->value = kmalloc(len + 1, GFP_KERNEL); + if (!payload->value) + return -ENOMEM; + + (void)memset(payload->value, 0, len + 1); + if (strncpy(payload->value, value, len) > 0) + payload->value[len] = '\0'; + + return 0; +} + +int hievent_set_time(struct hiview_hievent *event, long long seconds) +{ + if ((!event) || (seconds == 0)) { + pr_err("Bad input for %s", __func__); + return -EINVAL; + } + event->time = seconds; + return 0; +} + +static int append_array_item(char **pool, int pool_len, const char *path) +{ + int i; + + if ((!path) || (path[0] == 0)) { + pr_err("Bad path %s", __func__); + return -EINVAL; + } + + if (strlen(path) > MAX_PATH_LEN) { + pr_err("file path over max: %d", MAX_PATH_LEN); + return -EINVAL; + } + + for (i = 0; i < pool_len; i++) { + if (pool[i] != 0) + continue; + + pool[i] = kstrdup(path, GFP_KERNEL); + if (!pool[i]) + return -ENOMEM; + + break; + } + + if (i == MAX_PATH_NUMBER) { + pr_err("Too many paths"); + return -EINVAL; + } + + return 0; +} + +int hievent_add_filepath(struct hiview_hievent *event, const char *path) +{ + if (!event) { + pr_err("Bad path %s", __func__); + return -EINVAL; + } + return append_array_item(event->file_path, MAX_PATH_NUMBER, path); +} + +/* make string ":" to "::", ";" to ";;", and remove newline character + * for example: "abc:def;ghi" transfer to "abc::def;;ghi" + */ +static char *hievent_make_regular(char *value) +{ + int count = 0; + int len = 0; + char *temp = value; + char *regular = NULL; + char *regular_tmp = NULL; + size_t regular_len; + + while (*temp != '\0') { + if (*temp == ':') + count++; + else if (*temp == ';') + count++; + else if ((*temp == '\n') || (*temp == '\r')) + *temp = ' '; + + temp++; + len++; + } + + /* no need to transfer, just return old value */ + if (count == 0) + return value; + + regular_len = len + count * 2 + 1; // 2 char in a byte + regular = kmalloc(regular_len, GFP_KERNEL); + if (!regular) + return NULL; + + (void)memset(regular, 0, regular_len); + regular_tmp = regular; + temp = value; + while (*temp != 0) { + if ((*temp == ':') || (*temp == ';')) + *regular_tmp++ = *temp; + + *regular_tmp++ = *temp; + temp++; + } + *regular_tmp = '\0'; + + return regular; +} + +int logbuff_to_exception(char category, int level, char log_type, + char sn, const char *msg, int msglen) +{ + struct idap_header *hdr = NULL; + size_t buf_len = sizeof(int) + sizeof(struct idap_header) + msglen; + int ret; + int *check_code = NULL; + char *buffer = kmalloc(buf_len, GFP_KERNEL); + + if (!buffer) + return -ENOMEM; + + check_code = (int *)buffer; + *check_code = CHECK_CODE; + + hdr = (struct idap_header *)(buffer + sizeof(int)); + hdr->level = level; + hdr->category = category; + hdr->log_type = log_type; + hdr->sn = sn; + + memcpy(buffer + sizeof(int) + sizeof(struct idap_header), msg, msglen); + + ret = hievent_write_internal(buffer, buf_len); + + kfree(buffer); + + return ret; +} + +static int hievent_fill_payload(struct hiview_hievent *event, char **pbuf, + char *tmp, int length) +{ + struct hievent_payload *p = event->head; + int len = length; + int tmplen; + unsigned int keycount = 0; + + while (p) { + char *value = NULL; + char *regular_value = NULL; + int need_free = 1; + + if (!p->value) { + p = p->next; + continue; + } + if (keycount == 0) { + tmplen = snprintf(tmp, len - 1, " --extra "); + BUF_POINTER_FORWARD; + } + keycount++; + + /* fill key */ + if (p->key) + tmplen = snprintf(tmp, len - 1, "%s:", p->key); + + BUF_POINTER_FORWARD; + /* fill value */ + tmplen = 0; + + value = p->value; + regular_value = hievent_make_regular(value); + if (!regular_value) { + regular_value = "NULL"; + need_free = 0; + } + tmplen = snprintf(tmp, len - 1, "%s;", regular_value); + if ((value != regular_value) && need_free) + kfree(regular_value); + + BUF_POINTER_FORWARD; + p = p->next; + } + return len; +} + +static int hievent_convert_string(struct hiview_hievent *event, char **pbuf) +{ + int len; + char *tmp = NULL; + int tmplen; + unsigned int i; + + char *buf = kmalloc(EVENT_INFO_BUF_LEN, GFP_KERNEL); + + if (!buf) { + *pbuf = NULL; + return 0; + } + + (void)memset(buf, 0, EVENT_INFO_BUF_LEN); + len = EVENT_INFO_BUF_LEN; + tmp = buf; + + /* fill eventid */ + tmplen = snprintf(tmp, len - 1, "eventid %d", event->eventid); + BUF_POINTER_FORWARD; + + /* fill the path */ + for (i = 0; i < MAX_PATH_NUMBER; i++) { + if (!event->file_path[i]) + break; + + tmplen = snprintf(tmp, len - 1, " -i %s", event->file_path[i]); + BUF_POINTER_FORWARD; + } + + /* fill time */ + if (event->time) { + tmplen = snprintf(tmp, len - 1, " -t %lld", event->time); + BUF_POINTER_FORWARD; + } + + /* fill the payload info */ + len = hievent_fill_payload(event, pbuf, tmp, len); + *pbuf = buf; + return (EVENT_INFO_BUF_LEN - len); +} + +#define IDAP_LOGTYPE_CMD 1 +static int hievent_write_logexception(char *str, const int strlen) +{ + char tempchr; + char *strptr = str; + int left_buf_len = strlen + 1; + int sent_cnt = 0; + + while (left_buf_len > 0) { + if (left_buf_len > EVENT_INFO_PACK_BUF_LEN) { + tempchr = strptr[EVENT_INFO_PACK_BUF_LEN - 1]; + strptr[EVENT_INFO_PACK_BUF_LEN - 1] = '\0'; + logbuff_to_exception(0, 0, IDAP_LOGTYPE_CMD, 1, strptr, + EVENT_INFO_PACK_BUF_LEN); + left_buf_len -= (EVENT_INFO_PACK_BUF_LEN - 1); + strptr += (EVENT_INFO_PACK_BUF_LEN - 1); + strptr[0] = tempchr; + sent_cnt++; + } else { + logbuff_to_exception(0, 0, IDAP_LOGTYPE_CMD, 0, strptr, + left_buf_len); + sent_cnt++; + break; + } + } + + return sent_cnt; +} + +int hievent_report(struct hiview_hievent *obj) +{ + char *str = NULL; + int buf_len; + int sent_packet; + + if (!obj) { + pr_err("Bad event %s", __func__); + return -EINVAL; + } + + buf_len = hievent_convert_string(obj, &str); + if (!str) + return -EINVAL; + + sent_packet = hievent_write_logexception(str, buf_len); + pr_err("report: %s", str); + kfree(str); + + return sent_packet; +} + +void hievent_destroy(struct hiview_hievent *event) +{ + int i; + struct hievent_payload *p = NULL; + + if (!event) + return; + + p = event->head; + while (p) { + struct hievent_payload *del = p; + + p = p->next; + hievent_payload_destroy(del); + } + + event->head = NULL; + for (i = 0; i < MAX_PATH_NUMBER; i++) { + kfree(event->file_path[i]); + event->file_path[i] = NULL; + } + + kfree(event); +} diff --git a/drivers/staging/hievent/hiview_hievent.h b/drivers/staging/hievent/hiview_hievent.h new file mode 100644 index 0000000000000000000000000000000000000000..c1c003510485bb098de8d0c469be52befa0a0cc8 --- /dev/null +++ b/drivers/staging/hievent/hiview_hievent.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2021 Huawei Technologies Co., Ltd. All rights reserved. + */ + +#ifndef HIVIEW_HIEVENT_H +#define HIVIEW_HIEVENT_H + +#define MAX_PATH_NUMBER 10 + +/* hievent struct */ +struct hiview_hievent { + unsigned int eventid; + + long long time; + + /* payload linked list */ + struct hievent_payload *head; + + /* file path needs uploaded */ + char *file_path[MAX_PATH_NUMBER]; +}; + +struct hiview_hievent *hievent_create(unsigned int eventid); +int hievent_put_integer(struct hiview_hievent *event, + const char *key, long value); +int hievent_put_string(struct hiview_hievent *event, + const char *key, const char *value); +int hievent_set_time(struct hiview_hievent *event, long long seconds); +int hievent_add_filepath(struct hiview_hievent *event, const char *path); +int hievent_report(struct hiview_hievent *obj); +void hievent_destroy(struct hiview_hievent *event); + +#endif /* HIVIEW_HIEVENT_H */ diff --git a/drivers/staging/hilog/Kconfig b/drivers/staging/hilog/Kconfig new file mode 100644 index 0000000000000000000000000000000000000000..243934c4cc4c079e7438424f401ae603dbe002cd --- /dev/null +++ b/drivers/staging/hilog/Kconfig @@ -0,0 +1,22 @@ +# +# Sensor device configuration +# + +config HILOG + tristate "Hilog support" + default n + help + hilog buffer manager. + + Hilog is a simple log manager for OpenHarmonyOS. + log string write to /dev/hilog, and the hilog driver copy it + to the ring buffer. Ring buffer can be read from userspace. + + If unsure, say N. + +config HILOG_BUFFER_SIZE + int "hilog buffer size" + depends on HILOG + default 4096 + help + Define the default ring buffer size of hilog diff --git a/drivers/staging/hilog/Makefile b/drivers/staging/hilog/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..e53c86a5dfcd4d37d746631965ef0f31d9b99e20 --- /dev/null +++ b/drivers/staging/hilog/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for the hi hilog drivers. +# + +obj-$(CONFIG_HILOG) += hilog.o diff --git a/drivers/staging/hilog/hilog.c b/drivers/staging/hilog/hilog.c new file mode 100644 index 0000000000000000000000000000000000000000..178dcb6dd5b1b4553226ac6e3e0223db90dee53d --- /dev/null +++ b/drivers/staging/hilog/hilog.c @@ -0,0 +1,408 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 Huawei Technologies Co., Ltd. All rights reserved. + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef HILOGDEV_MAJOR +#define HILOGDEV_MAJOR 245 +#endif + +#ifndef HILOG_NR_DEVS +#define HILOG_NR_DEVS 2 +#endif + +#ifndef MEMDEV_SIZE +#define MEMDEV_SIZE 4096 +#endif + +static int hilog_major = HILOGDEV_MAJOR; + +module_param(hilog_major, int, 0444); + +struct cdev g_hilog_cdev; + +#define HILOG_BUFFER ((size_t)CONFIG_HILOG_BUFFER_SIZE) +#define HILOG_DRIVER "/dev/hilog" + +struct hilog_entry { + unsigned int len; + unsigned int header_size; + unsigned int pid : 16; + unsigned int task_id : 16; + unsigned int sec; + unsigned int nsec; + unsigned int reserved; + char msg[0]; +}; + +static ssize_t hilog_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos); +static ssize_t hilog_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos); + +static const struct file_operations hilog_fops = { + .read = hilog_read, + .write = hilog_write, +}; + +struct hilog_char_device { + int flag; + struct mutex mtx; /* lock to protect read/write buffer */ + unsigned char *buffer; + wait_queue_head_t wq; + size_t wr_off; + size_t hdr_off; + size_t size; + size_t count; +} hilog_dev; + +static inline unsigned char *hilog_buffer_head(void) +{ + return hilog_dev.buffer + hilog_dev.hdr_off; +} + +static void hilog_buffer_inc(size_t sz) +{ + if (hilog_dev.size + sz <= HILOG_BUFFER) { + hilog_dev.size += sz; + hilog_dev.wr_off += sz; + hilog_dev.wr_off %= HILOG_BUFFER; + hilog_dev.count++; + } +} + +static void hilog_buffer_dec(size_t sz) +{ + if (hilog_dev.size >= sz) { + hilog_dev.size -= sz; + hilog_dev.hdr_off += sz; + hilog_dev.hdr_off %= HILOG_BUFFER; + hilog_dev.count--; + } +} + +static int hilog_read_ring_buff(unsigned char __user *buffer, size_t buf_len) +{ + size_t retval; + size_t buf_left = HILOG_BUFFER - hilog_dev.hdr_off; + + if (buf_left > buf_len) { + retval = copy_to_user(buffer, hilog_buffer_head(), buf_len); + } else { + size_t mem_len = (buf_len > buf_left) ? buf_left : buf_len; + + retval = copy_to_user(buffer, hilog_buffer_head(), mem_len); + if (retval < 0) + return retval; + + retval = copy_to_user(buffer + buf_left, hilog_dev.buffer, + buf_len - buf_left); + } + return retval; +} + +static int hilog_read_ring_head_buffer(unsigned char *buffer, size_t buf_len) +{ + size_t buf_left = HILOG_BUFFER - hilog_dev.hdr_off; + + if (buf_left > buf_len) { + memcpy(buffer, hilog_buffer_head(), buf_len); + } else { + size_t mem_len = (buf_len > buf_left) ? buf_left : buf_len; + + memcpy(buffer, hilog_buffer_head(), mem_len); + memcpy(buffer + buf_left, hilog_dev.buffer, buf_len - buf_left); + } + + return 0; +} + +static ssize_t hilog_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + size_t retval; + struct hilog_entry header; + + (void)file; + if (wait_event_interruptible(hilog_dev.wq, (hilog_dev.size > 0))) + return -EINVAL; + + (void)mutex_lock(&hilog_dev.mtx); + + if (hilog_dev.size == 0) { + retval = 0; + goto out; + } + + retval = hilog_read_ring_head_buffer((unsigned char *)&header, + sizeof(header)); + if (retval < 0) { + retval = -EINVAL; + goto out; + } + + if (count < header.len + sizeof(header)) { + pr_err("buffer too small,buf_len=%d, header.len=%d,%d\n", + (int)count, header.len, header.header_size); + retval = -ENOMEM; + goto out; + } + + hilog_buffer_dec(sizeof(header)); + retval = copy_to_user((unsigned char *)user_buf, + (unsigned char *)&header, + min(count, sizeof(header))); + + if (retval < 0) { + retval = -EINVAL; + goto out; + } + + retval = hilog_read_ring_buff((unsigned char *) + (user_buf + sizeof(header)), + header.len); + if (retval < 0) { + retval = -EINVAL; + goto out; + } + + hilog_buffer_dec(header.len); + retval = header.len + sizeof(header); +out: + if (retval == -ENOMEM) { + // clean ring buffer + hilog_dev.wr_off = 0; + hilog_dev.hdr_off = 0; + hilog_dev.size = 0; + hilog_dev.count = 0; + } + (void)mutex_unlock(&hilog_dev.mtx); + + return retval; +} + +static int hilog_write_ring_buffer(unsigned char __user *buffer, size_t buf_len) +{ + int retval; + size_t buf_left = HILOG_BUFFER - hilog_dev.wr_off; + + if (buf_len > buf_left) { + retval = copy_from_user(hilog_dev.buffer + hilog_dev.wr_off, + buffer, buf_left); + if (retval) + return -1; + retval = copy_from_user(hilog_dev.buffer, buffer + buf_left, + min(HILOG_BUFFER, buf_len - buf_left)); + } else { + retval = copy_from_user(hilog_dev.buffer + hilog_dev.wr_off, + buffer, min(buf_left, buf_len)); + } + + if (retval < 0) + return -1; + + return 0; +} + +static int hilog_write_ring_head_buffer(unsigned char *buffer, size_t buf_len) +{ + size_t buf_left = HILOG_BUFFER - hilog_dev.wr_off; + + if (buf_len > buf_left) { + memcpy(hilog_dev.buffer + hilog_dev.wr_off, + buffer, buf_left); + memcpy(hilog_dev.buffer, buffer + buf_left, + min(HILOG_BUFFER, buf_len - buf_left)); + } else { + memcpy(hilog_dev.buffer + hilog_dev.wr_off, + buffer, min(buf_left, buf_len)); + } + + return 0; +} + +static void hilog_head_init(struct hilog_entry *header, size_t len) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0) +#define NANOSEC_PER_MIRCOSEC 1000 + struct timeval now = { 0 }; + + do_gettimeofday(&now); + + header->sec = now.tv_sec; + header->nsec = now.tv_usec * NANOSEC_PER_MIRCOSEC; +#else + struct timespec64 now = { 0 }; + + ktime_get_real_ts64(&now); + + header->sec = now.tv_sec; + header->nsec = now.tv_nsec; +#endif + + header->len = len; + header->pid = current->pid; + header->task_id = current->tgid; + header->header_size = sizeof(struct hilog_entry); +} + +static void hilog_cover_old_log(size_t buf_len) +{ + int retval; + struct hilog_entry header; + size_t total_size = buf_len + sizeof(struct hilog_entry); + static int drop_log_lines; + static bool is_last_time_full; + bool is_this_time_full = false; + + while (total_size + hilog_dev.size > HILOG_BUFFER) { + retval = hilog_read_ring_head_buffer((unsigned char *)&header, + sizeof(header)); + if (retval < 0) + break; + + drop_log_lines++; + is_this_time_full = true; + is_last_time_full = true; + hilog_buffer_dec(sizeof(header) + header.len); + } + if (is_last_time_full && !is_this_time_full) { + /* so we can only print one log if hilog ring buffer is full in a short time */ + if (drop_log_lines > 0) + pr_info("hilog ringbuffer full, drop %d line(s) log\n", + drop_log_lines); + is_last_time_full = false; + drop_log_lines = 0; + } +} + +int hilog_write_internal(const char __user *buffer, size_t buf_len) +{ + struct hilog_entry header; + int retval; + + (void)mutex_lock(&hilog_dev.mtx); + hilog_cover_old_log(buf_len); + hilog_head_init(&header, buf_len); + + retval = hilog_write_ring_head_buffer((unsigned char *)&header, + sizeof(header)); + if (retval) { + retval = -ENODATA; + goto out; + } + hilog_buffer_inc(sizeof(header)); + + retval = hilog_write_ring_buffer((unsigned char *)(buffer), header.len); + if (retval) { + retval = -ENODATA; + goto out; + } + + hilog_buffer_inc(header.len); + + retval = header.len; + +out: + (void)mutex_unlock(&hilog_dev.mtx); + if (retval > 0) + wake_up_interruptible(&hilog_dev.wq); + else if (retval < 0) + pr_err("write fail retval=%d\n", retval); + + return retval; +} + +static ssize_t hilog_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + (void)file; + if (count + sizeof(struct hilog_entry) > HILOG_BUFFER) { + pr_err("input too large\n"); + return -ENOMEM; + } + + return hilog_write_internal(user_buf, count); +} + +static void hilog_device_init(void) +{ + hilog_dev.buffer = kmalloc(HILOG_BUFFER, GFP_KERNEL); + if (!hilog_dev.buffer) + return; + + init_waitqueue_head(&hilog_dev.wq); + mutex_init(&hilog_dev.mtx); + hilog_dev.wr_off = 0; + hilog_dev.hdr_off = 0; + hilog_dev.size = 0; + hilog_dev.count = 0; +} + +static int __init hilogdev_init(void) +{ + int result; + dev_t devno = MKDEV(hilog_major, 0); + + result = register_chrdev_region(devno, 2, "hilog"); + if (result < 0) { + pr_emerg("\t register hilog error %d\n", result); + return result; + } + + cdev_init(&g_hilog_cdev, &hilog_fops); + g_hilog_cdev.owner = THIS_MODULE; + g_hilog_cdev.ops = &hilog_fops; + + cdev_add(&g_hilog_cdev, MKDEV(hilog_major, 0), HILOG_NR_DEVS); + + hilog_device_init(); + return 0; +} + +static void __exit hilog_exit_module(void) +{ + cdev_del(&g_hilog_cdev); + unregister_chrdev_region(MKDEV(hilog_major, 0), HILOG_NR_DEVS); +} + +static int __init hilog_init_module(void) +{ + int state = hilogdev_init(); + + pr_info("\t hilog_init Start%d\n", state); + return 0; +} + +module_init(hilog_init_module); +module_exit(hilog_exit_module); + +MODULE_AUTHOR("OHOS"); +MODULE_DESCRIPTION("User mode hilog device interface"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("hilog"); diff --git a/drivers/staging/hisysevent/Kconfig b/drivers/staging/hisysevent/Kconfig new file mode 100644 index 0000000000000000000000000000000000000000..a40621cb846a8786a035781d2e76aecc303cf039 --- /dev/null +++ b/drivers/staging/hisysevent/Kconfig @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 +config HISYSEVENT + tristate "Enable hisysevent" + depends on HIEVENT + help + Say Y here to enable hisysevent feature support. diff --git a/drivers/staging/hisysevent/Makefile b/drivers/staging/hisysevent/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..025a2349b47ed174324090ad521f823264476313 --- /dev/null +++ b/drivers/staging/hisysevent/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_HISYSEVENT) += hiview_hisysevent.o +# add lightweight writing feature +obj-$(CONFIG_HISYSEVENT) += hisysevent_builder.o +obj-$(CONFIG_HISYSEVENT) += hisysevent_raw_data_encoder.o +obj-$(CONFIG_HISYSEVENT) += hisysevent_raw_data.o diff --git a/drivers/staging/hisysevent/hisysevent_builder.c b/drivers/staging/hisysevent/hisysevent_builder.c new file mode 100644 index 0000000000000000000000000000000000000000..297f6e10af769035fe84fbdbd7310aacc0e3b9b1 --- /dev/null +++ b/drivers/staging/hisysevent/hisysevent_builder.c @@ -0,0 +1,363 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Huawei Technologies Co., Ltd. All rights reserved. + */ + +#include "hisysevent_builder.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_PARAM_NAME_LENGTH 48 + +#define PARAM_STR_MAX_LEN 1536 // 1.5KB +#define HISYSEVENT_INFO_BUF_LEN (2048 - 6) // 2KB - 6 (read_gap) + +#define TIME_ZONE_LEN 6 +#define TIME_ZONE_TOTAL_CNT 38 +#define DEFAULT_TZ_POS 14 + +#define MINUTE_TO_SECS 60 +#define SEC_TO_MILLISEC 1000 +#define MILLISEC_TO_NANOSEC (1000 * 1000) + +#define MAX_PARAM_NUMBER 128 + +#define HISYSEVENT_HEADER_SIZE sizeof(struct hisysevent_header) + +enum value_type { + /* int64_t */ + INT64 = 8, + + /* string */ + STRING = 12, +}; + +static int parse_time_zone(const char *time_zone_formatted) +{ + int ret; + + static const char *const time_zone_list[] = { + "-0100", "-0200", "-0300", "-0330", "-0400", "-0500", "-0600", + "-0700", "-0800", "-0900", "-0930", "-1000", "-1100", "-1200", + "+0000", "+0100", "+0200", "+0300", "+0330", "+0400", "+0430", + "+0500", "+0530", "+0545", "+0600", "+0630", "+0700", "+0800", + "+0845", "+0900", "+0930", "+1000", "+1030", "+1100", "+1200", + "+1245", "+1300", "+1400" + }; + if (!time_zone_formatted) + return DEFAULT_TZ_POS; + + ret = match_string(time_zone_list, ARRAY_SIZE(time_zone_list), time_zone_formatted); + if (ret < 0) + return DEFAULT_TZ_POS; + + return ret; +} + +static void hisysevent_builder_set_time(struct hisysevent_header *header) +{ + struct timespec64 ts; + struct timezone tz = sys_tz; + int tz_index = 0; + char time_zone[TIME_ZONE_LEN]; + int tz_hour; + int tz_min; + long long millisecs = 0; + + ktime_get_real_ts64(&ts); + millisecs = ts.tv_sec * SEC_TO_MILLISEC + ts.tv_nsec / MILLISEC_TO_NANOSEC; + header->timestamp = (u64)millisecs; + + tz_hour = (-tz.tz_minuteswest) / MINUTE_TO_SECS; + time_zone[tz_index++] = tz_hour >= 0 ? '+' : '-'; + tz_min = (-tz.tz_minuteswest) % MINUTE_TO_SECS; + sprintf(&time_zone[tz_index], "%02u%02u", abs(tz_hour), abs(tz_min)); + time_zone[TIME_ZONE_LEN - 1] = '\0'; + header->time_zone = (u8)parse_time_zone(time_zone); +} + +static bool is_valid_num_of_param(struct hisysevent_params *params) +{ + if (!params) + return false; + + return params->total_cnt < MAX_PARAM_NUMBER; +} + +static bool is_valid_string(const char *str, unsigned int max_len) +{ + unsigned int len = 0; + unsigned int i; + + if (!str) + return false; + + len = strlen(str); + if (len == 0 || len > max_len) + return false; + + if (!isalpha(str[0])) + return false; + + for (i = 1; i < len; i++) { + if (!isalnum(str[i]) && str[i] != '_') + return false; + } + return true; +} + +static int hisysevent_init_header(struct hisysevent_header *header, const char *domain, + const char *name, enum hisysevent_type type) +{ + if (!is_valid_string(domain, MAX_DOMAIN_LENGTH) || + !is_valid_string(name, MAX_EVENT_NAME_LENGTH)) { + pr_err("domain or name is invalid"); + return -EINVAL; + } + + strcpy(header->domain, domain); + strcpy(header->name, name); + + header->type = (u8)(type - 1); + header->pid = (u32)current->pid; + header->tid = (u32)current->tgid; + header->uid = (u32)current_uid().val; + header->is_open_trace = 0; // in kernel, this value is always 0 + + hisysevent_builder_set_time(header); + if (!(header->time_zone)) { + pr_err("failed to parse the time zone"); + goto init_error; + } + + pr_info("create hisysevent succeed, domain=%s, name=%s, type=%d", + header->domain, header->name, (header->type + 1)); + + return 0; + +init_error: + memset(header, 0, sizeof(*header)); + return -EINVAL; +} + +static int hisysevent_init_params(struct hisysevent_params *params) +{ + if (!params) { + pr_err("params is null"); + return -EINVAL; + } + + params->raw_data = raw_data_create(); + if (!(params->raw_data)) + return -EINVAL; + + params->total_cnt = 0; + return 0; +} + +static void hisysevent_params_destroy(struct hisysevent_params *params) +{ + if (!params) { + pr_err("params is null"); + return; + } + raw_data_destroy(params->raw_data); +} + +static bool hisysevent_check_params_validity(struct hisysevent_builder *builder) +{ + if (!builder) { + pr_err("builder is null"); + return false; + } + + if (!is_valid_num_of_param(&builder->params)) { + pr_err("number of param is invalid"); + return false; + } + + return true; +} + +struct hisysevent_builder* +hisysevent_builder_create(const char *domain, const char *name, enum hisysevent_type type) +{ + struct hisysevent_builder *builder; + + builder = kzalloc(sizeof(*builder), GFP_KERNEL); + if (!builder) + return NULL; + + // header struct initialize + if (hisysevent_init_header(&builder->header, domain, name, type) != 0) + goto create_err; + + // parameters struct initialize + if (hisysevent_init_params(&builder->params) != 0) + goto create_err; + + return builder; + +create_err: + hisysevent_builder_destroy(builder); + return NULL; +} +EXPORT_SYMBOL_GPL(hisysevent_builder_create); + +void hisysevent_builder_destroy(struct hisysevent_builder *builder) +{ + if (!builder) { + pr_err("try to destroy an invalid builder"); + return; + } + + // destroy hisysevent parameters + hisysevent_params_destroy(&builder->params); + + kfree(builder); +} +EXPORT_SYMBOL_GPL(hisysevent_builder_destroy); + +int hisysevent_builder_put_integer(struct hisysevent_builder *builder, const char *key, + s64 value) +{ + int ret; + struct hisysevent_raw_data *raw_data; + + if (!is_valid_string(key, MAX_PARAM_NAME_LENGTH)) { + pr_err("try to put an invalid key"); + return -EINVAL; + } + if (!hisysevent_check_params_validity(builder)) + return -EINVAL; + + raw_data = raw_data_create(); + if (!raw_data) { + pr_err("failed to create raw data for an new integer parameter"); + return -ENOMEM; + } + + ret = -EINVAL; + if ((str_length_delimited_encode(raw_data, key) != 0) || + (key_value_type_encode(raw_data, (u8)0, (u8)INT64, (u8)0) != 0) || + (int64_t_varint_encode(raw_data, value) != 0)) { + pr_err("failed to encode an integer parameter"); + goto put_int_err; + } + + if (raw_data_append(builder->params.raw_data, raw_data->data, raw_data->len) != 0) { + pr_err("failed to append a raw data"); + goto put_int_err; + } + + builder->params.total_cnt++; + ret = 0; + +put_int_err: + raw_data_destroy(raw_data); + return ret; +} +EXPORT_SYMBOL_GPL(hisysevent_builder_put_integer); + +int hisysevent_builder_put_string(struct hisysevent_builder *builder, const char *key, + const char *value) +{ + int ret; + struct hisysevent_raw_data *raw_data; + + if (!is_valid_string(key, MAX_PARAM_NAME_LENGTH)) { + pr_err("try to put an invalid key"); + return -EINVAL; + } + if (!value || strlen(value) > PARAM_STR_MAX_LEN) { + pr_err("string length exceeds limit"); + return -EINVAL; + } + if (!hisysevent_check_params_validity(builder)) + return -EINVAL; + + raw_data = raw_data_create(); + if (!raw_data) { + pr_err("failed to create raw data for a new string parameter"); + return -ENOMEM; + } + + ret = -EINVAL; + if ((str_length_delimited_encode(raw_data, key) != 0) || + (key_value_type_encode(raw_data, 0, (u8)STRING, 0) != 0) || + (str_length_delimited_encode(raw_data, value) != 0)) { + pr_err("failed to encode a string parameter"); + goto put_str_err; + } + + if (raw_data_append(builder->params.raw_data, raw_data->data, raw_data->len) != 0) { + pr_err("failed to append a raw data"); + goto put_str_err; + } + + builder->params.total_cnt++; + ret = 0; + +put_str_err: + raw_data_destroy(raw_data); + return ret; +} +EXPORT_SYMBOL_GPL(hisysevent_builder_put_string); + +int hisysevent_builder_build(struct hisysevent_builder *builder, + struct hisysevent_raw_data *raw_data) +{ + s32 blockSize; + struct hisysevent_raw_data *params_raw_data; + + if (!hisysevent_check_params_validity(builder)) + return -EINVAL; + + blockSize = 0; + // copy block size at first + if (raw_data_append(raw_data, (u8 *)(&blockSize), sizeof(s32)) != 0) { + pr_err("fialed to append block size"); + return -ENOMEM; + } + // copy header + if (raw_data_append(raw_data, (u8 *)(&builder->header), + sizeof(struct hisysevent_header)) != 0) { + pr_err("fialed to append sys event header"); + return -ENOMEM; + } + // copy total count of parameter + if (raw_data_append(raw_data, (u8 *)(&builder->params.total_cnt), + sizeof(s32)) != 0) { + pr_err("fialed to append total count of parameters"); + return -ENOMEM; + } + // copy customized parameters + params_raw_data = builder->params.raw_data; + if (!params_raw_data) { + pr_err("this sys event doesn't have any parameter"); + return -EINVAL; + } + if (raw_data_append(raw_data, params_raw_data->data, params_raw_data->len) != 0) { + pr_err("fialed to append encoded raw data of parameters"); + return -ENOMEM; + } + // update block size + blockSize = raw_data->len; + if (raw_data_update(raw_data, (u8 *)(&blockSize), sizeof(s32), 0) != 0) { + pr_err("fialed to update block size"); + return -ENOMEM; + } + return 0; +} +EXPORT_SYMBOL_GPL(hisysevent_builder_build); diff --git a/drivers/staging/hisysevent/hisysevent_builder.h b/drivers/staging/hisysevent/hisysevent_builder.h new file mode 100644 index 0000000000000000000000000000000000000000..6570b849984729789515b32dbd8c82f04d762bec --- /dev/null +++ b/drivers/staging/hisysevent/hisysevent_builder.h @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2023 Huawei Technologies Co., Ltd. All rights reserved. + */ + +#ifndef HISYSEVENT_BUILDER_H +#define HISYSEVENT_BUILDER_H + +#include + +#include +#include + +#include "hisysevent_raw_data_encoder.h" +#include "hisysevent_raw_data.h" + +#define MAX_DOMAIN_LENGTH 16 +#define MAX_EVENT_NAME_LENGTH 32 + +#pragma pack(1) + +struct hisysevent_header { + /* event domain */ + char domain[MAX_DOMAIN_LENGTH + 1]; + + /* event name */ + char name[MAX_EVENT_NAME_LENGTH + 1]; + + /* event timestamp */ + u64 timestamp; + + /* time zone */ + u8 time_zone; + + /* user id */ + u32 uid; + + /* process id */ + u32 pid; + + /* thread id */ + u32 tid; + + /* event hash code*/ + u64 id; + + /* event type */ + u8 type: 2; // enum hisysevent_type. + + /* trace info flag*/ + u8 is_open_trace: 1; +}; + +#pragma pack() + +struct hisysevent_params { + /* total count of parameters */ + s32 total_cnt; + + /* content of parameters */ + struct hisysevent_raw_data *raw_data; +}; + +/* hisysevent builder struct */ +struct hisysevent_builder { + /* common header */ + struct hisysevent_header header; + + /* customized parameters*/ + struct hisysevent_params params; +}; + +struct hisysevent_builder * +hisysevent_builder_create(const char *domain, const char *name, enum hisysevent_type type); + +void hisysevent_builder_destroy(struct hisysevent_builder *builder); + +int hisysevent_builder_put_integer(struct hisysevent_builder *builder, const char *key, + s64 value); + +int hisysevent_builder_put_string(struct hisysevent_builder *builder, const char *key, + const char *value); + +int hisysevent_builder_build(struct hisysevent_builder *builder, + struct hisysevent_raw_data *raw_data); + +#endif /* HISYSEVENT_BUILDER_H */ diff --git a/drivers/staging/hisysevent/hisysevent_raw_data.c b/drivers/staging/hisysevent/hisysevent_raw_data.c new file mode 100644 index 0000000000000000000000000000000000000000..5e37ad726e1686947b7db440c114e80c1c59f741 --- /dev/null +++ b/drivers/staging/hisysevent/hisysevent_raw_data.c @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Huawei Technologies Co., Ltd. All rights reserved. + */ + +#include "hisysevent_raw_data.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#define EXPAND_BUF_SIZE 100 + +static int raw_data_init(struct hisysevent_raw_data *raw_data) +{ + if (!raw_data) { + pr_err("raw data is null"); + return -EINVAL; + } + + raw_data->data = kzalloc(EXPAND_BUF_SIZE, GFP_KERNEL); + if (!(raw_data->data)) { + pr_err("failed to allocate memory for raw data"); + return -ENOMEM; + } + + raw_data->capacity = EXPAND_BUF_SIZE; + raw_data->len = 0; + + return 0; +} + +int raw_data_update(struct hisysevent_raw_data *dest, u8 *src, unsigned int len, + unsigned int pos) +{ + if (!dest) { + pr_err("try to update a data which is null"); + return -EINVAL; + } + if (!src || len == 0) { + pr_info("do nothing"); + return 0; + } + if (dest->len < pos) { + pr_err("try to update on an invalid position"); + return -EINVAL; + } + if ((pos + len) > dest->capacity) { + unsigned int expanded_size; + u8 *resize_data; + + expanded_size = (len > EXPAND_BUF_SIZE) ? len : EXPAND_BUF_SIZE; + resize_data = kmalloc(dest->capacity + expanded_size, GFP_KERNEL); + if (!resize_data) { + pr_err("failed to expand memory for raw data"); + return -ENOMEM; + } + if (dest->data) { + memcpy(resize_data, dest->data, dest->len); + dest->capacity += expanded_size; + kfree(dest->data); + } + dest->data = resize_data; + } + + // append new data + memcpy(dest->data + pos, src, len); + if ((pos + len) > dest->len) + dest->len = pos + len; + return 0; +} +EXPORT_SYMBOL_GPL(raw_data_update); + +int raw_data_append(struct hisysevent_raw_data *dest, u8 *src, unsigned int len) +{ + return raw_data_update(dest, src, len, dest->len); +} +EXPORT_SYMBOL_GPL(raw_data_append); + +struct hisysevent_raw_data* +raw_data_create(void) +{ + struct hisysevent_raw_data *raw_data; + + raw_data = kzalloc(sizeof(*raw_data), GFP_KERNEL); + if (!raw_data) + return NULL; + + if (raw_data_init(raw_data) != 0) + goto create_err; + + return raw_data; + +create_err: + raw_data_destroy(raw_data); + return NULL; +} +EXPORT_SYMBOL_GPL(raw_data_create); + +void raw_data_destroy(struct hisysevent_raw_data *raw_data) +{ + if (!raw_data) { + pr_err("try to destroy an invalid raw data"); + return; + } + + if (raw_data->data) + kfree(raw_data->data); + + kfree(raw_data); +} +EXPORT_SYMBOL_GPL(raw_data_destroy); diff --git a/drivers/staging/hisysevent/hisysevent_raw_data.h b/drivers/staging/hisysevent/hisysevent_raw_data.h new file mode 100644 index 0000000000000000000000000000000000000000..93a9b97068f80592f406f04e3d9a59d6c0ea2df5 --- /dev/null +++ b/drivers/staging/hisysevent/hisysevent_raw_data.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2023 Huawei Technologies Co., Ltd. All rights reserved. + */ + +#ifndef HISYSEVENT_RAW_DATA_H +#define HISYSEVENT_RAW_DATA_H + +#include +#include + +struct hisysevent_raw_data { + /* pointer to raw data */ + u8 *data; + + /* length of data wrote */ + int len; + + /* total allocated memory */ + int capacity; +}; + +struct hisysevent_raw_data * +raw_data_create(void); + +int raw_data_append(struct hisysevent_raw_data *dest, u8 *src, unsigned int len); + +int raw_data_update(struct hisysevent_raw_data *dest, u8 *src, unsigned int len, + unsigned int offset); + +void raw_data_destroy(struct hisysevent_raw_data *raw_data); + +#endif /* HISYSEVENT_RAW_DATA_H */ diff --git a/drivers/staging/hisysevent/hisysevent_raw_data_encoder.c b/drivers/staging/hisysevent/hisysevent_raw_data_encoder.c new file mode 100644 index 0000000000000000000000000000000000000000..a206950aa162ccbd67535fc9a1296477ba20b42a --- /dev/null +++ b/drivers/staging/hisysevent/hisysevent_raw_data_encoder.c @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Huawei Technologies Co., Ltd. All rights reserved. + */ + +#include "hisysevent_raw_data_encoder.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TAG_BYTE_OFFSET 5 +#define TAG_BYTE_BOUND (BIT(TAG_BYTE_OFFSET)) +#define TAG_BYTE_MASK (TAG_BYTE_BOUND - 1) + +#define NON_TAG_BYTE_OFFSET 7 +#define NON_TAG_BYTE_BOUND (BIT(NON_TAG_BYTE_OFFSET)) +#define NON_TAG_BYTE_MASK (NON_TAG_BYTE_BOUND - 1) + +enum hisysevent_encode_type { + // zigzag varint + VARINT = 0, + + // length delimited + LENGTH_DELIMITED = 1, +}; + +#pragma pack(1) + +struct param_value_type { + /* array flag */ + u8 is_array: 1; + + /* type of parameter value */ + u8 value_type: 4; + + /* byte count of parameter value */ + u8 value_byte_cnt: 3; +}; + +#pragma pack() + +static u8 encode_tag(u8 type) +{ + return type << (TAG_BYTE_OFFSET + 1); +} + +static int unsigned_varint_code(struct hisysevent_raw_data *data, + enum hisysevent_encode_type type, u64 val) +{ + u8 cpy_val; + + cpy_val = encode_tag((u8)type) | + ((val < TAG_BYTE_BOUND) ? 0 : TAG_BYTE_BOUND) | + (u8)(val & TAG_BYTE_MASK); + if (raw_data_append(data, (u8 *)(&cpy_val), sizeof(u8)) != 0) + return -EINVAL; + + val >>= TAG_BYTE_OFFSET; + while (val > 0) { + cpy_val = ((val < NON_TAG_BYTE_BOUND) ? 0 : NON_TAG_BYTE_BOUND) | + (u8)(val & NON_TAG_BYTE_MASK); + if (raw_data_append(data, (u8 *)(&cpy_val), sizeof(u8)) != 0) + return -EINVAL; + + val >>= NON_TAG_BYTE_OFFSET; + } + return 0; +} + +static int signed_varint_encode(struct hisysevent_raw_data *data, + enum hisysevent_encode_type type, s64 val) +{ + u64 uval; + + uval = (val << 1) ^ (val >> ((sizeof(val) << 3) - 1)); // zigzag encode + return unsigned_varint_code(data, type, uval); +} + +int key_value_type_encode(struct hisysevent_raw_data *data, u8 is_array, u8 type, + u8 count) +{ + struct param_value_type value_type; + + value_type.is_array = is_array; + value_type.value_type = type; + value_type.value_byte_cnt = count; + + if (raw_data_append(data, (u8 *)(&value_type), + sizeof(struct param_value_type)) != 0) + return -EINVAL; + + return 0; +} +EXPORT_SYMBOL_GPL(key_value_type_encode); + +int str_length_delimited_encode(struct hisysevent_raw_data *data, const char *str) +{ + u64 length; + + length = (u64)strlen(str); + if (unsigned_varint_code(data, LENGTH_DELIMITED, length) != 0) + return -EINVAL; + + if (raw_data_append(data, (u8 *)(str), length) != 0) + return -EINVAL; + + return 0; +} +EXPORT_SYMBOL_GPL(str_length_delimited_encode); + +int int64_t_varint_encode(struct hisysevent_raw_data *raw_data, s64 val) +{ + return signed_varint_encode(raw_data, VARINT, val); +} +EXPORT_SYMBOL_GPL(int64_t_varint_encode); diff --git a/drivers/staging/hisysevent/hisysevent_raw_data_encoder.h b/drivers/staging/hisysevent/hisysevent_raw_data_encoder.h new file mode 100644 index 0000000000000000000000000000000000000000..75cb7c3271bdff2c681479eb0643ea0912f1ec3a --- /dev/null +++ b/drivers/staging/hisysevent/hisysevent_raw_data_encoder.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2023 Huawei Technologies Co., Ltd. All rights reserved. + */ + +#ifndef HISYSEVENT_RAW_DATA_ENCODER_H +#define HISYSEVENT_RAW_DATA_ENCODER_H + +#include +#include + +#include "hisysevent_raw_data.h" + +int key_value_type_encode(struct hisysevent_raw_data *data, u8 is_array, u8 type, + u8 count); + +int str_length_delimited_encode(struct hisysevent_raw_data *data, const char *str); + +int int64_t_varint_encode(struct hisysevent_raw_data *data, s64 val); + +#endif /* HISYSEVENT_RAW_DATA_ENCODER_H */ diff --git a/drivers/staging/hisysevent/hiview_hisysevent.c b/drivers/staging/hisysevent/hiview_hisysevent.c new file mode 100644 index 0000000000000000000000000000000000000000..8503dd7ae644b1b50b5f85fcaba1faf4a8784123 --- /dev/null +++ b/drivers/staging/hisysevent/hiview_hisysevent.c @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022-2023 Huawei Technologies Co., Ltd. All rights reserved. + */ + +#include + +#ifdef CONFIG_HISYSEVENT + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hisysevent_builder.h" +#include "hisysevent_raw_data.h" + +#define HISYSEVENT_WRITER_DEV "/dev/bbox" + +static int CHECK_CODE = 0x7BCDABCD; + +#define HISYSEVENT_INFO_BUF_LEN (2048 - 6) // 2KB - 6 (read_gap) + +/* hisysevent struct */ +struct hiview_hisysevent { + /* hisysevent builder */ + struct hisysevent_builder *builder; +}; + +struct hiview_hisysevent * +hisysevent_create(const char *domain, const char *name, enum hisysevent_type type) +{ + struct hiview_hisysevent *event; + + event = kzalloc(sizeof(*event), GFP_KERNEL); + if (!event) + return NULL; + + event->builder = hisysevent_builder_create(domain, name, type); + if (!event->builder) + goto create_err; + return event; + +create_err: + hisysevent_destroy(&event); + return NULL; +} +EXPORT_SYMBOL_GPL(hisysevent_create); + +void hisysevent_destroy(struct hiview_hisysevent **event) +{ + if (!event || !*event) { + pr_err("invalid event"); + return; + } + + hisysevent_builder_destroy((*event)->builder); + + kfree(*event); + *event = NULL; +} +EXPORT_SYMBOL_GPL(hisysevent_destroy); + +int hisysevent_put_integer(struct hiview_hisysevent *event, const char *key, long long value) +{ + if (!event) { + pr_err("invalid event"); + return -EINVAL; + } + return hisysevent_builder_put_integer(event->builder, key, value); +} +EXPORT_SYMBOL_GPL(hisysevent_put_integer); + +int hisysevent_put_string(struct hiview_hisysevent *event, const char *key, const char *value) +{ + if (!event) { + pr_err("invalid event"); + return -EINVAL; + } + return hisysevent_builder_put_string(event->builder, key, value); +} +EXPORT_SYMBOL_GPL(hisysevent_put_string); + +int hisysevent_write(struct hiview_hisysevent *event) +{ + struct hisysevent_raw_data *raw_data; + int ret; + struct file *filp; + unsigned long vcount; + struct iovec vec[3]; +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 192) + mm_segment_t oldfs; +#endif + struct iov_iter iter; + + if (!event) { + pr_err("invalid event"); + return -EINVAL; + } + + raw_data = raw_data_create(); + if (!raw_data) { + pr_err("failed to create a new raw data"); + return -EINVAL; + } + + ret = hisysevent_builder_build(event->builder, raw_data); + if (ret != 0) { + pr_err("hisysevent builder build failed"); + goto event_wrote_err; + } + pr_info("total block size of hisysevent data is %d", raw_data->len); + + if (raw_data->len > HISYSEVENT_INFO_BUF_LEN) { + pr_err("content of sysevent exceeds limit"); + goto event_wrote_err; + } + + if (!current->fs) { + pr_err("file system is null"); + goto event_wrote_err; + } + + filp = filp_open(HISYSEVENT_WRITER_DEV, O_WRONLY, 0); + + if (!filp || IS_ERR(filp)) { + ret = PTR_ERR(filp); + pr_err("failed to access '%s', res=%d", HISYSEVENT_WRITER_DEV, ret); + goto event_wrote_err; + } + + vcount = 0; + vec[vcount].iov_base = &CHECK_CODE; + vec[vcount++].iov_len = sizeof(CHECK_CODE); + vec[vcount].iov_base = raw_data->data; + vec[vcount++].iov_len = raw_data->len + 1; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 192) + oldfs = get_fs(); + set_fs(KERNEL_DS); +#endif + iov_iter_init(&iter, WRITE, vec, vcount, iov_length(vec, vcount)); + ret = vfs_iter_write(filp, &iter, &filp->f_pos, 0); +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 192) + set_fs(oldfs); +#endif + + if (ret < 0) + pr_err("failed to write hisysevent, ret=%d", ret); + + filp_close(filp, NULL); + +event_wrote_err: + raw_data_destroy(raw_data); + return ret; +} +EXPORT_SYMBOL_GPL(hisysevent_write); + +#endif /* CONFIG_HISYSEVENT */ diff --git a/drivers/staging/hungtask/Kconfig b/drivers/staging/hungtask/Kconfig new file mode 100644 index 0000000000000000000000000000000000000000..4e80dc9fc4342075d1d592878dcfd3533e7ddcea --- /dev/null +++ b/drivers/staging/hungtask/Kconfig @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0 +config DFX_HUNGTASK + bool "DFX hungtask" + depends on DETECT_HUNG_TASK + default n + help + Base DFX hungtask module + +config DFX_HUNGTASK_USER + bool "DFX hungtask user watchdog module" + depends on DFX_HUNGTASK + default n + help + DFX hungtask user watchdog module \ No newline at end of file diff --git a/drivers/staging/hungtask/Makefile b/drivers/staging/hungtask/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..12def220e3d60cdcd8fa5530a36e5ab9b8ce9b1c --- /dev/null +++ b/drivers/staging/hungtask/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_DFX_HUNGTASK) += hungtask_base.o +obj-$(CONFIG_DFX_HUNGTASK_USER) += hungtask_user.o diff --git a/drivers/staging/hungtask/hungtask_base.c b/drivers/staging/hungtask/hungtask_base.c new file mode 100644 index 0000000000000000000000000000000000000000..30408c0ba1de6cc8374c133d6f434578d5ba5020 --- /dev/null +++ b/drivers/staging/hungtask/hungtask_base.c @@ -0,0 +1,1031 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 Huawei Technologies Co., Ltd. All rights reserved. + */ + +#define pr_fmt(fmt) "hungtask_base " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_DFX_ZEROHUNG +#include +#endif +#include +#include "hungtask_user.h" + +static struct rb_root list_tasks = RB_ROOT; +static DEFINE_SPINLOCK(list_tasks_lock); +static struct hlist_head whitelist[WHITELIST_LEN]; +static struct whitelist_item whitetmplist[WHITELIST_LEN]; +static bool whitelist_empty = true; +static int remove_cnt; +static struct task_item *remove_list[MAX_REMOVE_LIST_NUM + 1]; +static unsigned long __read_mostly hungtask_timeout_secs = + CONFIG_DEFAULT_HUNG_TASK_TIMEOUT; +static int did_panic; +static unsigned int hungtask_enable = HT_DISABLE; +static unsigned int whitelist_type = WHITE_LIST; +static int whitelist_dump_cnt = DEFAULT_WHITE_DUMP_CNT; +static int whitelist_panic_cnt = DEFAULT_WHITE_PANIC_CNT; +static int appspawn_pid; +static int dump_and_upload; +static int time_since_upload; +static int hung_task_must_panic; +static int report_zrhung_id; +static struct task_hung_upload upload; +static int do_refresh; +static char frozen_buf[FROZEN_BUF_LEN]; +static int frozen_used; +static bool frozed_head; +static unsigned long cur_heartbeat; +static struct work_struct send_work; +static char report_buf_text[REPORT_MSGLENGTH]; + +bool hashlist_find(struct hlist_head *head, int count, pid_t tgid) +{ + struct hashlist_node *hnode = NULL; + + if (count <= 0) + return false; + if (hlist_empty(&head[tgid % count])) + return false; + hlist_for_each_entry(hnode, &head[tgid % count], list) { + if (hnode->pid == tgid) + return true; + } + return false; +} + +void hashlist_clear(struct hlist_head *head, int count) +{ + int i = 0; + struct hlist_node *n = NULL; + struct hashlist_node *hnode = NULL; + + for (i = 0; i < count; i++) { + hlist_for_each_entry_safe(hnode, n, &head[i], list) { + hlist_del(&hnode->list); + kfree(hnode); + hnode = NULL; + } + } + for (i = 0; i < count; i++) + INIT_HLIST_HEAD(&head[i]); +} + +bool hashlist_insert(struct hlist_head *head, int count, pid_t tgid) +{ + struct hashlist_node *hnode = NULL; + + if (hashlist_find(head, count, tgid)) + return false; + hnode = kmalloc(sizeof(struct hashlist_node), GFP_ATOMIC); + if (!hnode) + return false; + INIT_HLIST_NODE(&hnode->list); + hnode->pid = tgid; + hlist_add_head(&hnode->list, &head[tgid % count]); + return true; +} + +static bool rcu_lock_break(struct task_struct *g, struct task_struct *t) +{ + bool can_cont = false; + + get_task_struct(g); + get_task_struct(t); + rcu_read_unlock(); + cond_resched(); + rcu_read_lock(); + can_cont = pid_alive(g) && pid_alive(t); + put_task_struct(t); + put_task_struct(g); + return can_cont; +} + +static bool rcu_break(int *max_count, int *batch_count, + struct task_struct *g, + struct task_struct *t) +{ + if (!(*max_count)--) + return true; + if (!--(*batch_count)) { + *batch_count = HUNG_TASK_BATCHING; + if (!rcu_lock_break(g, t)) + return true; + } + return false; +} + +static pid_t get_pid_by_name(const char *name) +{ + int max_count = PID_MAX_LIMIT; + int batch_count = HUNG_TASK_BATCHING; + struct task_struct *g = NULL; + struct task_struct *t = NULL; + int pid = 0; + + rcu_read_lock(); + do_each_thread(g, t) { + if (rcu_break(&max_count, &batch_count, g, t)) + goto unlock; + if (!strncmp(t->comm, name, TASK_COMM_LEN)) { + pid = t->tgid; + goto unlock; + } + } while_each_thread(g, t); + +unlock: + rcu_read_unlock(); + return pid; +} + +static unsigned int get_task_type(pid_t pid, pid_t tgid, struct task_struct *parent) +{ + unsigned int flag = TASK_TYPE_IGNORE; + /* check tgid of it's parent as PPID */ + if (parent) { + pid_t ppid = parent->tgid; + + if (ppid == PID_KTHREAD) + flag |= TASK_TYPE_KERNEL; + else if (ppid == appspawn_pid) + flag |= TASK_TYPE_APP; + else if (ppid == PID_INIT) + flag |= TASK_TYPE_NATIVE; + } + if (!whitelist_empty && hashlist_find(whitelist, WHITELIST_LEN, tgid)) + flag |= TASK_TYPE_WHITE | TASK_TYPE_JANK; + + return flag; +} + +static void refresh_appspawn_pids(void) +{ + int max_count = PID_MAX_LIMIT; + int batch_count = HUNG_TASK_BATCHING; + struct task_struct *g = NULL; + struct task_struct *t = NULL; + + rcu_read_lock(); + do_each_thread(g, t) { + if (rcu_break(&max_count, &batch_count, g, t)) + goto unlock; + if (!strncmp(t->comm, "appspawn", TASK_COMM_LEN)) + appspawn_pid = t->tgid; + } while_each_thread(g, t); +unlock: + rcu_read_unlock(); +} + +static void refresh_task_type(pid_t pid, int task_type) +{ + struct task_item *item = NULL; + struct rb_node *p = NULL; + + spin_lock(&list_tasks_lock); + for (p = rb_first(&list_tasks); p; p = rb_next(p)) { + item = rb_entry(p, struct task_item, node); + if (item->tgid == pid) + item->task_type = task_type; + } + spin_unlock(&list_tasks_lock); +} + +static void refresh_whitelist_pids(void) +{ + int i; + + hashlist_clear(whitelist, WHITELIST_LEN); + for (i = 0; i < WHITELIST_LEN; i++) { + if (!strlen(whitetmplist[i].name)) + continue; + whitetmplist[i].pid = + get_pid_by_name(whitetmplist[i].name); + if (!whitetmplist[i].pid) + continue; + refresh_task_type(whitetmplist[i].pid, + TASK_TYPE_WHITE | TASK_TYPE_JANK); + if (hashlist_insert(whitelist, WHITELIST_LEN, + whitetmplist[i].pid)) + pr_info("whitelist[%d]-%s-%d\n", i, + whitetmplist[i].name, whitetmplist[i].pid); + else + pr_info("can't find %s\n", whitetmplist[i].name); + } + refresh_appspawn_pids(); +} + +static struct task_item *find_task(pid_t pid, struct rb_root *root) +{ + struct rb_node **p = &root->rb_node; + struct task_item *cur = NULL; + struct rb_node *parent = NULL; + + while (*p) { + parent = *p; + cur = rb_entry(parent, struct task_item, node); + if (!cur) + return NULL; + if (pid < cur->pid) + p = &(*p)->rb_left; + else if (pid > cur->pid) + p = &(*p)->rb_right; + else + return cur; + } + return NULL; +} + +static bool insert_task(struct task_item *item, struct rb_root *root) +{ + struct rb_node **p = &root->rb_node; + struct rb_node *parent = NULL; + struct task_item *cur = NULL; + + while (*p) { + parent = *p; + + cur = rb_entry(parent, struct task_item, node); + if (!cur) + return false; + if (item->pid < cur->pid) { + p = &(*p)->rb_left; + } else if (item->pid > cur->pid) { + p = &(*p)->rb_right; + } else { + pr_info("insert pid=%d,tgid=%d,name=%s,type=%d fail\n", + item->pid, item->tgid, + item->name, item->task_type); + return false; + } + } + rb_link_node(&item->node, parent, p); + rb_insert_color(&item->node, root); + return true; +} + +void show_block_task(struct task_item *taskitem, struct task_struct *p) +{ + unsigned long last_arrival; + unsigned long last_queued; + +#ifdef CONFIG_SCHED_INFO + last_arrival = p->sched_info.last_arrival; + last_queued = p->sched_info.last_queued; +#else + last_arrival = 0; + last_queued = 0; +#endif /* CONFIG_SCHED_INFO */ + if (unlikely(p->flags & PF_FROZEN)) { + if (taskitem) + pr_err("name=%s,PID=%d,tgid=%d,tgname=%s," + "FROZEN for %ds,type=%d,la%lu/lq%lu\n", + p->comm, p->pid, p->tgid, + p->group_leader->comm, + taskitem->d_state_time * HEARTBEAT_TIME, + taskitem->task_type, + last_arrival, last_queued); + else + pr_err("name=%s,PID=%d,tgid=%d,tgname=%s," + "just FROZE,la%lu/lq%lu\n", + p->comm, p->pid, p->tgid, + p->group_leader->comm, + last_arrival, last_queued); + } else { + if (taskitem) + pr_err("name=%s,PID=%d,tgid=%d,prio=%d,cpu=%d,tgname=%s," + "type=%d,blocked for %ds,la%lu/lq%lu\n", + taskitem->name, taskitem->pid, p->tgid, p->prio, + task_cpu(p), p->group_leader->comm, taskitem->task_type, + taskitem->d_state_time * HEARTBEAT_TIME, + last_arrival, last_queued); + else + pr_err("name=%s,PID=%d,tgid=%d,prio=%d,cpu=%d," + "tgname=%s,la%lu/lq%lu\n", + p->comm, p->pid, p->tgid, p->prio, task_cpu(p), + p->group_leader->comm, + last_arrival, last_queued); + + sched_show_task(p); + } +} + +void htbase_show_state_filter(unsigned long state_filter) +{ + struct task_struct *g = NULL; + struct task_struct *p = NULL; + struct task_item *taskitem = NULL; + +#if BITS_PER_LONG == 32 + pr_info(" task PC stack pid father\n"); +#else + pr_info(" task PC stack pid father\n"); +#endif + rcu_read_lock(); + for_each_process_thread(g, p) { + /* + * reset the NMI-timeout, listing all files on a slow + * console might take a lot of time: + */ + touch_nmi_watchdog(); + if ((p->__state == TASK_RUNNING) || (p->__state & state_filter)) { + spin_lock(&list_tasks_lock); + taskitem = find_task(p->pid, &list_tasks); + spin_unlock(&list_tasks_lock); + show_block_task(taskitem, p); + } + } + touch_all_softlockup_watchdogs(); + rcu_read_unlock(); + /* Show locks if hungtask happen */ + if ((state_filter == TASK_UNINTERRUPTIBLE) || !state_filter) + debug_show_all_locks(); +} + +void hungtask_show_state_filter(unsigned long state_filter) +{ + pr_err("BinderChain_SysRq start\n"); + htbase_show_state_filter(state_filter); + pr_err("BinderChain_SysRq end\n"); +} + +void do_dump_task(struct task_struct *task) +{ + sched_show_task(task); + debug_show_held_locks(task); +} + +void do_show_task(struct task_struct *task, unsigned int flag, int d_state_time) +{ + pr_err("%s, flag=%d\n", __func__, flag); + rcu_read_lock(); + if (!pid_alive(task)) { + rcu_read_unlock(); + return; + } + if (flag & (FLAG_DUMP_WHITE | FLAG_DUMP_APP)) { + int cnt = 0; + + trace_sched_process_hang(task); + cnt = d_state_time; + pr_err("INFO: task %s:%d tgid:%d blocked for %ds in %s\n", + task->comm, task->pid, task->tgid, + (HEARTBEAT_TIME * cnt), + (flag & FLAG_DUMP_WHITE) ? "whitelist" : "applist"); + pr_err(" %s %s %.*s\n", + print_tainted(), init_utsname()->release, + (int)strcspn(init_utsname()->version, " "), + init_utsname()->version); + do_dump_task(task); + touch_nmi_watchdog(); + if (flag & FLAG_DUMP_WHITE && (!dump_and_upload)) { + dump_and_upload++; + upload.pid = task->pid; + upload.tgid = task->tgid; + upload.duration = d_state_time; + memset(upload.name, 0, sizeof(upload.name)); + strncpy(upload.name, task->comm, sizeof(upload.name)); + upload.flag = flag; + if (task->flags & PF_FROZEN) + upload.flag = (upload.flag | FLAG_PF_FROZEN); + } + } + rcu_read_unlock(); +} + +static void do_panic(void) +{ + if (sysctl_hung_task_panic) { + trigger_all_cpu_backtrace(); + panic("hungtask: blocked tasks"); + } +} + +static void create_taskitem(struct task_item *taskitem, + struct task_struct *task) +{ + taskitem->pid = task->pid; + taskitem->tgid = task->tgid; + memset(taskitem->name, 0, sizeof(taskitem->name)); + strncpy(taskitem->name, task->comm, sizeof(taskitem->name)); + taskitem->switch_count = task->nvcsw + task->nivcsw; + taskitem->dump_wa = 0; /* whitelist or applist task dump times */ + taskitem->panic_wa = 0; /* whitelist or applist task panic times */ + taskitem->d_state_time = -1; + taskitem->isdone_wa = true; /* if task in white or app dealed */ +} + +static bool refresh_task(struct task_item *taskitem, struct task_struct *task) +{ + bool is_called = false; + + if (taskitem->switch_count != (task->nvcsw + task->nivcsw)) { + taskitem->switch_count = task->nvcsw + task->nivcsw; + is_called = true; + return is_called; + } + if (taskitem->task_type & TASK_TYPE_WHITE) { + taskitem->isdone_wa = false; + taskitem->dump_wa++; + taskitem->panic_wa++; + } + taskitem->d_state_time++; + if (task->flags & PF_FROZEN) + taskitem->task_type |= TASK_TYPE_FROZEN; + return is_called; +} + +static void remove_list_tasks(struct task_item *item) +{ + rb_erase(&item->node, &list_tasks); + kfree(item); +} + +static void shrink_process_item(struct task_item *item, bool *is_finish) +{ + if (remove_cnt >= MAX_REMOVE_LIST_NUM) { + int i; + + remove_list[remove_cnt++] = item; + for (i = 0; i < remove_cnt; i++) + remove_list_tasks(remove_list[i]); + remove_cnt = 0; + *is_finish = false; + } else { + remove_list[remove_cnt++] = item; + } +} + +static void shrink_list_tasks(void) +{ + int i; + bool is_finish = false; + struct rb_node *n = NULL; + struct task_item *item = NULL; + + spin_lock(&list_tasks_lock); + while (!is_finish) { + is_finish = true; + for (n = rb_first(&list_tasks); n != NULL; n = rb_next(n)) { + item = rb_entry(n, struct task_item, node); + if (!item) + continue; + if (item->isdone_wa) { + shrink_process_item(item, &is_finish); + if (!is_finish) + break; + } + } + } + for (i = 0; i < remove_cnt; i++) + remove_list_tasks(remove_list[i]); + remove_cnt = 0; + spin_unlock(&list_tasks_lock); +} + +static void check_parameters(void) +{ + if ((whitelist_dump_cnt < 0) || + (whitelist_dump_cnt > DEFAULT_WHITE_DUMP_CNT)) + whitelist_dump_cnt = DEFAULT_WHITE_DUMP_CNT; + if ((whitelist_panic_cnt <= 0) || + (whitelist_panic_cnt > DEFAULT_WHITE_PANIC_CNT)) + whitelist_panic_cnt = DEFAULT_WHITE_PANIC_CNT; +} + +static void send_work_handler(struct work_struct *data) +{ +#ifdef CONFIG_DFX_ZEROHUNG + zrhung_send_event(HUNGTASK_DOMAIN, HUNGTASK_NAME, + report_buf_text); +#endif +} + +static void htbase_report_zrhung_event(const char *report_buf_tag) +{ + htbase_show_state_filter(TASK_UNINTERRUPTIBLE); + pr_err("%s end\n", report_buf_tag); + schedule_work(&send_work); + report_zrhung_id++; +} + +static void htbase_report_zrhung(unsigned int event) +{ + bool report_load = false; + char report_buf_tag[REPORT_MSGLENGTH] = {0}; + char report_name[TASK_COMM_LEN + 1] = {0}; + int report_pid = 0; + int report_hungtime = 0; + int report_tasktype = 0; + + if (!event) + return; + if (event & HUNGTASK_EVENT_WHITELIST) { + snprintf(report_buf_tag, sizeof(report_buf_tag), + "hungtask_whitelist_%d", report_zrhung_id); + strncpy(report_name, upload.name, TASK_COMM_LEN); + report_pid = upload.pid; + report_tasktype = TASK_TYPE_WHITE; + report_hungtime = whitelist_dump_cnt * HEARTBEAT_TIME; + report_load = true; + } else { + pr_err("No such event report to zerohung!"); + } + pr_err("%s start\n", report_buf_tag); + if (event & HUNGTASK_EVENT_WHITELIST) + pr_err("report HUNGTASK_EVENT_WHITELIST to zrhung\n"); + if (upload.flag & FLAG_PF_FROZEN) + snprintf(report_buf_text, sizeof(report_buf_text), + "Task %s(%s) pid %d type %d blocked %ds.", + report_name, "FROZEN", report_pid, report_tasktype, report_hungtime); + else + snprintf(report_buf_text, sizeof(report_buf_text), + "Task %s pid %d type %d blocked %ds.", + report_name, report_pid, report_tasktype, report_hungtime); + if (report_load) + htbase_report_zrhung_event(report_buf_tag); +} + +static int print_frozen_list_item(int pid) +{ + int tmp; + + if (!frozed_head) { + tmp = snprintf(frozen_buf, FROZEN_BUF_LEN, "%s", "FROZEN Pid:"); + if (tmp < 0) + return -1; + frozen_used += min(tmp, FROZEN_BUF_LEN - 1); + frozed_head = true; + } + tmp = snprintf(frozen_buf + frozen_used, FROZEN_BUF_LEN - frozen_used, "%d,", + pid); + if (tmp < 0) + return -1; + frozen_used += min(tmp, FROZEN_BUF_LEN - frozen_used - 1); + return frozen_used; +} + +int dump_task_wa(struct task_item *item, int dump_cnt, + struct task_struct *task, unsigned int flag) +{ + int ret = 0; + + if ((item->d_state_time > TWO_MINUTES) && + (item->d_state_time % TWO_MINUTES != 0)) + return ret; + if ((item->d_state_time > HUNG_TEN_MINUTES) && + (item->d_state_time % HUNG_TEN_MINUTES != 0)) + return ret; + if ((item->d_state_time > HUNG_ONE_HOUR) && + (item->d_state_time % HUNG_ONE_HOUR != 0)) + return ret; + if (dump_cnt && (item->dump_wa > dump_cnt)) { + item->dump_wa = 1; + if (!dump_and_upload && task->flags & PF_FROZEN) { + int tmp = print_frozen_list_item(item->pid); + if (tmp < 0) + return ret; + if (tmp >= FROZEN_BUF_LEN - 1) { + pr_err("%s", frozen_buf); + memset(frozen_buf, 0, sizeof(frozen_buf)); + frozen_used = 0; + frozed_head = false; + print_frozen_list_item(item->pid); + } + } else if (!dump_and_upload) { + pr_err("Ready to dump a task %s\n", item->name); + do_show_task(task, flag, item->d_state_time); + ret++; + } + } + return ret; +} + +static void update_panic_task(struct task_item *item) +{ + if (upload.pid != 0) + return; + + upload.pid = item->pid; + upload.tgid = item->tgid; + memset(upload.name, 0, sizeof(upload.name)); + strncpy(upload.name, item->name, sizeof(upload.name)); +} + +static void deal_task(struct task_item *item, struct task_struct *task, bool is_called) +{ + int any_dumped_num = 0; + + if (is_called) { + item->dump_wa = 1; + item->panic_wa = 1; + item->d_state_time = 0; + return; + } + if (item->task_type & TASK_TYPE_WHITE) + any_dumped_num = dump_task_wa(item, whitelist_dump_cnt, task, + FLAG_DUMP_WHITE); + if (!is_called && (item->task_type & TASK_TYPE_WHITE)) { + if (whitelist_panic_cnt && item->panic_wa > whitelist_panic_cnt) { + pr_err("Task %s is causing panic\n", item->name); + update_panic_task(item); + item->panic_wa = 0; + hung_task_must_panic++; + } else { + item->isdone_wa = false; + } + } + if (item->isdone_wa) + remove_list_tasks(item); +} + +static bool check_conditions(struct task_struct *task, unsigned int task_type) +{ + bool no_check = true; + + if (task->flags & PF_FROZEN) + return no_check; + if (task_type & TASK_TYPE_WHITE && + (whitelist_dump_cnt || whitelist_panic_cnt)) + no_check = false; + return no_check; +} + +static void htbase_check_one_task(struct task_struct *t) +{ + unsigned int task_type = TASK_TYPE_IGNORE; + unsigned long switch_count = t->nvcsw + t->nivcsw; + struct task_item *taskitem = NULL; + bool is_called = false; + + if (unlikely(!switch_count)) { + pr_info("skip one's switch_count is zero\n"); + return; + } + + taskitem = find_task(t->pid, &list_tasks); + if (taskitem) { + if (check_conditions(t, taskitem->task_type)) + return; + is_called = refresh_task(taskitem, t); + } else { + task_type = get_task_type(t->pid, t->tgid, t->real_parent); + if (check_conditions(t, task_type)) + return; + taskitem = kmalloc(sizeof(*taskitem), GFP_ATOMIC); + if (!taskitem) { + pr_err("kmalloc failed"); + return; + } + memset(taskitem, 0, sizeof(*taskitem)); + taskitem->task_type = task_type; + create_taskitem(taskitem, t); + is_called = refresh_task(taskitem, t); + insert_task(taskitem, &list_tasks); + } + deal_task(taskitem, t, is_called); +} + +static void htbase_pre_process(void) +{ + htbase_set_timeout_secs(sysctl_hung_task_timeout_secs); + cur_heartbeat++; + if ((cur_heartbeat % REFRESH_INTERVAL) == 0) + do_refresh = 1; + else + do_refresh = 0; + if (do_refresh || (cur_heartbeat < TIME_REFRESH_PIDS)) { + refresh_whitelist_pids(); + check_parameters(); + } +} + +static void htbase_post_process(void) +{ + struct rb_node *n = NULL; + unsigned int hungevent = 0; + + if (frozen_used) { + pr_err("%s", frozen_buf); + memset(frozen_buf, 0, sizeof(frozen_buf)); + frozen_used = 0; + frozed_head = false; + } + if (dump_and_upload == HUNG_TASK_UPLOAD_ONCE) { + hungevent |= HUNGTASK_EVENT_WHITELIST; + dump_and_upload++; + } + if (dump_and_upload > 0) { + time_since_upload++; + if (time_since_upload > (whitelist_panic_cnt - whitelist_dump_cnt)) { + dump_and_upload = 0; + time_since_upload = 0; + } + } + if (hung_task_must_panic) { + htbase_show_state_filter(TASK_UNINTERRUPTIBLE); + hung_task_must_panic = 0; + pr_err("Task %s:%d blocked for %ds is causing panic\n", + upload.name, upload.pid, + whitelist_panic_cnt * HEARTBEAT_TIME); + do_panic(); + } + htuser_post_process_userlist(); + shrink_list_tasks(); + for (n = rb_first(&list_tasks); n != NULL; n = rb_next(n)) { + struct task_item *item = rb_entry(n, struct task_item, node); + item->isdone_wa = true; + } + + if (hungevent) + htbase_report_zrhung(hungevent); +} + +void htbase_check_tasks(unsigned long timeout) +{ + int max_count = PID_MAX_LIMIT; + int batch_count = HUNG_TASK_BATCHING; + struct task_struct *g = NULL; + struct task_struct *t = NULL; + + if (!hungtask_enable) + return; + if (test_taint(TAINT_DIE) || did_panic) { + pr_err("already in doing panic\n"); + return; + } + + htbase_pre_process(); + rcu_read_lock(); + for_each_process_thread(g, t) { + if (!max_count--) + goto unlock; + if (!--batch_count) { + batch_count = HUNG_TASK_BATCHING; + if (!rcu_lock_break(g, t)) + goto unlock; + } + if ((t->__state == TASK_UNINTERRUPTIBLE) || + (t->__state == TASK_KILLABLE)) + htbase_check_one_task(t); + } +unlock: + rcu_read_unlock(); + htbase_post_process(); +} + +static ssize_t htbase_enable_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + if (hungtask_enable) + return snprintf(buf, ENABLE_SHOW_LEN, "on\n"); + else + return snprintf(buf, ENABLE_SHOW_LEN, "off\n"); +} + +static ssize_t htbase_enable_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + char tmp[6]; /* only storage "on" "off" "kick" and enter */ + size_t len; + char *p = NULL; + + if (!buf) + return -EINVAL; + if ((count < 2) || (count > (sizeof(tmp) - 1))) { + pr_err("string too long or too short\n"); + return -EINVAL; + } + + p = memchr(buf, '\n', count); + len = p ? (size_t)(p - buf) : count; + memset(tmp, 0, sizeof(tmp)); + strncpy(tmp, buf, len); + if (!strncmp(tmp, "on", strlen(tmp))) { + hungtask_enable = HT_ENABLE; + pr_info("set hungtask_enable to enable\n"); + } else if (!strncmp(tmp, "off", strlen(tmp))) { + hungtask_enable = HT_DISABLE; + pr_info("set hungtask_enable to disable\n"); + } else { + pr_err("only accept on or off\n"); + } + return (ssize_t) count; +} + +static ssize_t htbase_monitorlist_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + int i; + char *start = buf; + char all_buf[WHITELIST_STORE_LEN - 20]; /* exclude extra header len 20*/ + unsigned long len = 0; + + memset(all_buf, 0, sizeof(all_buf)); + for (i = 0; i < WHITELIST_LEN; i++) { + if (whitetmplist[i].pid > 0) { + len += snprintf(all_buf + len, sizeof(all_buf) - len, + "%s-%d,", whitetmplist[i].name, whitetmplist[i].pid); + if (!(len < sizeof(all_buf))) { + len = sizeof(all_buf) - 1; + break; + } + } + } + if (len > 0) + all_buf[len] = 0; + if (whitelist_type == WHITE_LIST) + buf += snprintf(buf, WHITELIST_STORE_LEN, "whitelist:[%s]\n", all_buf); + else if (whitelist_type == BLACK_LIST) + buf += snprintf(buf, WHITELIST_STORE_LEN, "blacklist:[%s]\n", all_buf); + else + buf += snprintf(buf, WHITELIST_STORE_LEN, "\n"); + return buf - start; +} + +static void htbase_monitorlist_update(char **cur) +{ + int index = 0; + char *token = NULL; + + hashlist_clear(whitelist, WHITELIST_LEN); + memset(whitetmplist, 0, sizeof(whitetmplist)); + /* generate the new whitelist */ + for (; ; ) { + token = strsep(cur, ","); + if (token && strlen(token)) { + strncpy(whitetmplist[index].name, token, TASK_COMM_LEN); + if (strlen(whitetmplist[index].name) > 0) + whitelist_empty = false; + index++; + if (index >= WHITELIST_LEN) + break; + } + if (!(*cur)) + break; + } +} + +/* + * monitorlist_store - Called when 'write/echo' method is + * used on entry '/sys/kernel/hungtask/monitorlist'. + */ +static ssize_t htbase_monitorlist_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + size_t len; + char *p = NULL; + char all_buf[WHITELIST_STORE_LEN]; + char *cur = all_buf; + + + if ((n < 2) || (n > (sizeof(all_buf) - 1))) { + pr_err("whitelist input string illegal\n"); + return -EINVAL; + } + if (!buf) + return -EINVAL; + /* + * input format: + * write /sys/kernel/hungtask/monitorlist "whitelist, + * system_server,surfaceflinger" + */ + p = memchr(buf, '\n', n); + len = p ? (size_t)(p - buf) : n; /* exclude the '\n' */ + + memset(all_buf, 0, sizeof(all_buf)); + len = len > WHITELIST_STORE_LEN ? WHITELIST_STORE_LEN : len; + strncpy(all_buf, buf, len); + p = strsep(&cur, ","); + if (!cur) { + pr_err("string is not correct\n"); + return -EINVAL; + } + if (!strncmp(p, "whitelist", n)) { + whitelist_type = WHITE_LIST; + } else { + if (!strncmp(p, "blacklist", n)) + pr_err("blacklist is not support\n"); + else + pr_err("wrong list type is set\n"); + return -EINVAL; + } + if (!strlen(cur)) { + pr_err("at least one process need to be set\n"); + return -EINVAL; + } + pr_err("whitelist is %s\n", cur); + + htbase_monitorlist_update(&cur); + /* check again in case user input "whitelist,,,,,," */ + if (whitelist_empty) { + pr_err("at least one process need to be set\n"); + return -EINVAL; + } + return (ssize_t) n; +} + +/* used for sysctl at "/proc/sys/kernel/hung_task_timeout_secs" */ +void htbase_set_timeout_secs(unsigned long new_hungtask_timeout_secs) +{ + if ((new_hungtask_timeout_secs > CONFIG_DEFAULT_HUNG_TASK_TIMEOUT) || + (new_hungtask_timeout_secs % HEARTBEAT_TIME)) + return; + hungtask_timeout_secs = new_hungtask_timeout_secs; + /* + * if user change panic timeout value, we sync it to dump value + * defaultly, user can set it diffrently + */ + whitelist_panic_cnt = (int)(hungtask_timeout_secs / HEARTBEAT_TIME); + if (whitelist_panic_cnt > THIRTY_SECONDS) + whitelist_dump_cnt = whitelist_panic_cnt / HT_DUMP_IN_PANIC_LOOSE; + else + whitelist_dump_cnt = whitelist_panic_cnt / HT_DUMP_IN_PANIC_STRICT; +} + +void htbase_set_panic(int new_did_panic) +{ + did_panic = new_did_panic; +} + +static struct kobj_attribute timeout_attribute = { + .attr = { + .name = "enable", + .mode = 0640, + }, + .show = htbase_enable_show, + .store = htbase_enable_store, +}; + +static struct kobj_attribute monitorlist_attr = { + .attr = { + .name = "monitorlist", + .mode = 0640, + }, + .show = htbase_monitorlist_show, + .store = htbase_monitorlist_store, +}; + +#ifdef CONFIG_DFX_HUNGTASK_USER +static struct kobj_attribute userlist_attr = { + .attr = { + .name = "userlist", + .mode = 0640, + }, + .show = htuser_list_show, + .store = htuser_list_store, +}; +#endif + +static struct attribute *attrs[] = { + &timeout_attribute.attr, + &monitorlist_attr.attr, +#ifdef CONFIG_DFX_HUNGTASK_USER + &userlist_attr.attr, +#endif + NULL +}; + +static struct attribute_group hungtask_attr_group = { + .attrs = attrs, +}; + +static struct kobject *hungtask_kobj; +int htbase_create_sysfs(void) +{ + int i; + int ret; + + /* sleep 1000ms and wait /sys/kernel ready */ + while (!kernel_kobj) + msleep(1000); + + /* Create kobject named "hungtask" located at /sys/kernel/huangtask */ + hungtask_kobj = kobject_create_and_add("hungtask", kernel_kobj); + if (!hungtask_kobj) + return -ENOMEM; + ret = sysfs_create_group(hungtask_kobj, &hungtask_attr_group); + if (ret) + kobject_put(hungtask_kobj); + + for (i = 0; i < WHITELIST_LEN; i++) + INIT_HLIST_HEAD(&whitelist[i]); + memset(whitetmplist, 0, sizeof(whitetmplist)); + + INIT_WORK(&send_work, send_work_handler); + + return ret; +} diff --git a/drivers/staging/hungtask/hungtask_user.c b/drivers/staging/hungtask/hungtask_user.c new file mode 100644 index 0000000000000000000000000000000000000000..b66905672159d9d9515794234d07669827735058 --- /dev/null +++ b/drivers/staging/hungtask/hungtask_user.c @@ -0,0 +1,260 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 Huawei Technologies Co., Ltd. All rights reserved. + */ + +#define pr_fmt(fmt) "hungtask_user " fmt + +#include +#include +#include +#include +#include + +#include + +#define CMD_MIN_LEN 3 +#define CMD_MAX_LEN 20 +#define USERLIST_NUM 10 +#define MAX_USER_TIMEOUT 120 +#define MAX_SHOW_LEN 512 + +struct user_item { + pid_t pid; + int cur_cnt; + int panic_cnt; +}; + +static struct user_item userlist[USERLIST_NUM]; +static int userlist_count; +static DEFINE_SPINLOCK(userlist_lock); +static bool is_registered; +static bool need_panic; +static bool need_dump; +static int block_time; +static int block_pid; + +static void htuser_show_task(int pid) +{ + struct task_struct *p = NULL; + + p = pid_task(find_vpid(pid), PIDTYPE_PID); + if (p == NULL) { + pr_err("can not find pid %d\n", pid); + return; + } + + if (p->flags & PF_FROZEN) { + pr_info("process %d is frozen\n", pid); + return; + } + if (p->__state == TASK_UNINTERRUPTIBLE) { + pr_err("UserList_KernelStack start\n"); + sched_show_task(p); + pr_err("UserList_KernelStack end\n"); + } +} + +static void htuser_list_insert(int pid, int count) +{ + spin_lock(&userlist_lock); + if (userlist_count >= USERLIST_NUM) { + pr_err("list is full\n"); + spin_unlock(&userlist_lock); + return; + } + userlist[userlist_count].pid = pid; + userlist[userlist_count].cur_cnt = 0; + userlist[userlist_count].panic_cnt = count; + userlist_count++; + spin_unlock(&userlist_lock); +} + +static int htuser_list_remove(int pid) +{ + int i; + + spin_lock(&userlist_lock); + for (i = 0; i < userlist_count; i++) { + if (userlist[i].pid == pid) { + if (i == userlist_count - 1) { + memset(&userlist[i], 0, sizeof(userlist[i])); + } else { + int len = sizeof(userlist[0]) * (userlist_count - i - 1); + memmove(&userlist[i], &userlist[i + 1], len); + } + userlist_count--; + spin_unlock(&userlist_lock); + return 0; + } + } + spin_unlock(&userlist_lock); + return -ENOENT; +} + +static void htuser_list_update(void) +{ + int i; + + need_panic = false; + need_dump = false; + spin_lock(&userlist_lock); + for (i = 0; i < userlist_count; i++) { + userlist[i].cur_cnt++; + if ((userlist[i].cur_cnt >= userlist[i].panic_cnt) || + (userlist[i].cur_cnt == userlist[i].panic_cnt / 2)) { + htuser_show_task(userlist[i].pid); + pr_err("process %d not scheduled for %ds\n", + userlist[i].pid, + userlist[i].cur_cnt * HEARTBEAT_TIME); + } + if (userlist[i].cur_cnt == userlist[i].panic_cnt) { + need_dump = true; + need_panic = true; + block_time = userlist[i].cur_cnt * HEARTBEAT_TIME; + block_pid = userlist[i].pid; + } + } + spin_unlock(&userlist_lock); +} + +static void htuser_list_kick(int pid) +{ + int i; + + spin_lock(&userlist_lock); + for (i = 0; i < userlist_count; i++) { + if (userlist[i].pid == pid) { + userlist[i].cur_cnt = 0; + spin_unlock(&userlist_lock); + return; + } + } + spin_unlock(&userlist_lock); +} + +void htuser_post_process_userlist(void) +{ + htuser_list_update(); + if (need_dump) { + pr_err("print all cpu stack and D state stack\n"); + hungtask_show_state_filter(TASK_UNINTERRUPTIBLE); + } + if (need_panic) + panic("UserList Process %d blocked for %ds causing panic", block_pid, block_time); +} + +static int htuser_process_notifier(struct notifier_block *self, + unsigned long cmd, void *v) +{ + struct task_struct *task = v; + + if (task == NULL) + return NOTIFY_OK; + + if ((task->tgid == task->pid) && (!htuser_list_remove(task->tgid))) + pr_err("remove success due to process %d die\n", task->tgid); + + return NOTIFY_OK; +} + +static struct notifier_block htuser_process_notify = { + .notifier_call = htuser_process_notifier, +}; + +ssize_t htuser_list_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int i; + char tmp[MAX_SHOW_LEN] = {0}; + int len = 0; + + len += snprintf(tmp + len, MAX_SHOW_LEN - len, + " Pid Current(sec) Expired(sec)\n"); + + spin_lock(&userlist_lock); + for (i = 0; i < userlist_count; i++) { + len += snprintf(tmp + len, MAX_SHOW_LEN - len, + "%5d %5d %5d", userlist[i].pid, + userlist[i].cur_cnt * HEARTBEAT_TIME, + userlist[i].panic_cnt * HEARTBEAT_TIME); + if (len >= MAX_SHOW_LEN) { + len = MAX_SHOW_LEN - 1; + break; + } + } + spin_unlock(&userlist_lock); + pr_info("%s\n", tmp); + strncpy(buf, tmp, len); + + return len; +} + +static int htuser_list_store_on(char *tmp, size_t len, int pid) +{ + unsigned long sec = 0; + + if (kstrtoul(tmp + 3, 10, &sec)) { + pr_err("invalid timeout value\n"); + return -EINVAL; + } + if ((sec > MAX_USER_TIMEOUT) || !sec) { + pr_err("invalid timeout value, should be in 0-%d\n", MAX_USER_TIMEOUT); + return -EINVAL; + } + if (sec % HEARTBEAT_TIME) { + pr_err("invalid timeout value, should be devided by %d\n", HEARTBEAT_TIME); + return -EINVAL; + } + pr_info("process %d set to enable, timeout=%ld\n", pid, sec); + htuser_list_insert(pid, sec / HEARTBEAT_TIME); + if (!is_registered) { + profile_event_register(PROFILE_TASK_EXIT, + &htuser_process_notify); + is_registered = true; + } + + return 0; +} + +ssize_t htuser_list_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + char tmp[CMD_MAX_LEN]; /* on/off/kick */ + size_t len; + char *p = NULL; + int pid = current->tgid; + int uid = current->cred->euid.val; + + if (uid >= 10000) + pr_err("non-system process %d(uid=%d) can not be added to hungtask userlist\n", + pid, uid); + if ((count < CMD_MIN_LEN) || (count > CMD_MAX_LEN)) { + pr_err("string too long or too short\n"); + return -EINVAL; + } + if (!buf) + return -EINVAL; + + memset(tmp, 0, sizeof(tmp)); + p = memchr(buf, '\n', count); + len = p ? (size_t)(p - buf) : count; + strncpy(tmp, buf, len); + + if (strncmp(tmp, "on", CMD_MIN_LEN) == 0) { + if (htuser_list_store_on(tmp, len, pid)) + return -EINVAL; + } else if (unlikely(strncmp(tmp, "off", CMD_MIN_LEN) == 0)) { + pr_info("process %d set to disable\n", pid); + if (!htuser_list_remove(pid)) + pr_err("remove success duet to process %d call off\n", pid); + } else if (likely(strncmp(tmp, "kick", CMD_MIN_LEN) == 0)) { + pr_info("process %d is kicked\n", pid); + htuser_list_kick(pid); + } else { + pr_err("only accept on off or kick\n"); + } + return (ssize_t)count; +} + diff --git a/drivers/staging/hungtask/hungtask_user.h b/drivers/staging/hungtask/hungtask_user.h new file mode 100644 index 0000000000000000000000000000000000000000..17ea7212b21ea551d9b776ecd0e40fb87d9ce954 --- /dev/null +++ b/drivers/staging/hungtask/hungtask_user.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2022 Huawei Technologies Co., Ltd. All rights reserved. + */ + +#ifndef DFX_HUNGTASK_USER_H +#define DFX_HUNGTASK_USER_H + +#include + +#ifdef CONFIG_DFX_HUNGTASK_USER +void htuser_post_process_userlist(void); +ssize_t htuser_list_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count); +ssize_t htuser_list_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf); +#else +static inline void htuser_post_process_userlist(void) +{ +} + +static inline ssize_t htuser_list_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + return 0; +} +static inline ssize_t htuser_list_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + return 0; +} + +#endif + +#endif /* DFX_HUNGTASK_USER_H */ diff --git a/drivers/staging/zerohung/Kconfig b/drivers/staging/zerohung/Kconfig new file mode 100644 index 0000000000000000000000000000000000000000..913d28efb4cccbf0bbdc423ab66cdbd284fe049e --- /dev/null +++ b/drivers/staging/zerohung/Kconfig @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 +config DFX_ZEROHUNG + bool "zerohung driver" + default n + depends on HISYSEVENT + help + This feature support to catch hung log diff --git a/drivers/staging/zerohung/Makefile b/drivers/staging/zerohung/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..3727a0e91dca7dd51a88f539ea56bb0e36f19a21 --- /dev/null +++ b/drivers/staging/zerohung/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_DFX_ZEROHUNG) += zrhung_event.o watchpoint/ diff --git a/drivers/staging/zerohung/watchpoint/Makefile b/drivers/staging/zerohung/watchpoint/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..1cb8d7f99417f130d16ae56b3287bad594f01df0 --- /dev/null +++ b/drivers/staging/zerohung/watchpoint/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_DFX_ZEROHUNG) += hung_wp_screen.o diff --git a/drivers/staging/zerohung/watchpoint/hung_wp_screen.c b/drivers/staging/zerohung/watchpoint/hung_wp_screen.c new file mode 100644 index 0000000000000000000000000000000000000000..3b5f2d6daa5401af661000f1bb6b15076ada2a06 --- /dev/null +++ b/drivers/staging/zerohung/watchpoint/hung_wp_screen.c @@ -0,0 +1,299 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 Huawei Technologies Co., Ltd. All rights reserved. + */ + +#define pr_fmt(fmt) "zrhung " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TIME_CONVERT_UNIT 1000 +#define DEFAULT_TIMEOUT 10 + +#define LPRESSEVENT_TIME 5 +#define POWERKEYEVENT_MAX_COUNT 10 +#define POWERKEYEVENT_DEFAULT_COUNT 3 +#define POWERKEYEVENT_DEFAULT_TIMEWINDOW 5 +#define POWERKEYEVENT_DEFAULT_LIMIT_MS 300 +#define POWERKEYEVENT_DEFAULT_REPORT_MIN 2 +#define POWERKEYEVENT_TIME_LEN (POWERKEYEVENT_MAX_COUNT + 2) + +struct hung_wp_screen_data { + struct timer_list timer; + struct timer_list long_press_timer; + struct workqueue_struct *workq; + struct work_struct send_work; + spinlock_t lock; + int fb_blank; + int check_id; + int tag_id; +}; + +static bool init_done; +static struct hung_wp_screen_data g_hung_data; +static unsigned int lastreport_time; +static unsigned int lastprkyevt_time; +static unsigned int powerkeyevent_time[POWERKEYEVENT_TIME_LEN] = {0}; +static unsigned int newevt; +static unsigned int headevt; +static int *check_off_point; +struct work_struct powerkeyevent_sendwork; +struct work_struct lpressevent_sendwork; +static struct notifier_block hung_wp_screen_setblank_ncb; + +static void zrhung_lpressevent_send_work(struct work_struct *work) +{ + pr_info("LONGPRESS_EVENT send to zerohung\n"); + zrhung_send_event(WP_SCREEN_DOMAIN, WP_SCREEN_LPRESS_NAME, "none"); +} + +static void zrhung_wp_lpress_send(struct timer_list *t) +{ + int *check_off = check_off_point; + + del_timer(&g_hung_data.long_press_timer); + *check_off = 0; + queue_work(g_hung_data.workq, &lpressevent_sendwork); +} + +static void zrhung_powerkeyevent_send_work(struct work_struct *work) +{ + pr_info("POWERKEY_EVENT send to zerohung\n"); + zrhung_send_event(WP_SCREEN_DOMAIN, WP_SCREEN_PWK_NAME, "none"); +} + +static void zrhung_powerkeyevent_report(unsigned int dur, unsigned int end) +{ + unsigned int send_interval; + + send_interval = end > lastreport_time ? + ((end - lastreport_time) / TIME_CONVERT_UNIT) : POWERKEYEVENT_DEFAULT_REPORT_MIN; + if (unlikely(lastreport_time == 0)) { + lastreport_time = end; + } else if (send_interval < POWERKEYEVENT_DEFAULT_REPORT_MIN) { + pr_info("powerkeyevent too fast to report: %d\n", end); + return; + } + lastreport_time = end; + queue_work(g_hung_data.workq, &powerkeyevent_sendwork); +} + +static unsigned int refresh_prkyevt_index(unsigned int event) +{ + unsigned int evt = event; + + if (evt < POWERKEYEVENT_MAX_COUNT) + evt++; + else + evt = 0; + return evt; +} + +static void zrhung_new_powerkeyevent(unsigned int tmescs) +{ + unsigned int prkyevt_interval; + unsigned int evt_index; + int diff; + + powerkeyevent_time[newevt] = tmescs; + evt_index = (newevt >= headevt) ? + (newevt - headevt) : (newevt + POWERKEYEVENT_MAX_COUNT + 1 - headevt); + if (evt_index < (POWERKEYEVENT_DEFAULT_COUNT - 1)) { + pr_info("powerkeyevent not enough-%d\n", POWERKEYEVENT_DEFAULT_COUNT); + } else { + diff = powerkeyevent_time[newevt] - powerkeyevent_time[headevt]; + if (diff < 0) { + pr_info("powerkeyevent sth wrong in record time\n"); + return; + } + + prkyevt_interval = (unsigned int)(diff / TIME_CONVERT_UNIT); + if (prkyevt_interval <= POWERKEYEVENT_DEFAULT_TIMEWINDOW) + zrhung_powerkeyevent_report(prkyevt_interval, tmescs); + headevt = refresh_prkyevt_index(headevt); + } + newevt = refresh_prkyevt_index(newevt); +} + +static void zrhung_powerkeyevent_handler(void) +{ + unsigned int curtime; + unsigned long curjiff; + + pr_info("powerkeyevent check start"); + curjiff = jiffies; + curtime = jiffies_to_msecs(curjiff); + if (unlikely(lastprkyevt_time > curtime)) { + pr_info("powerkeyevent check but time overflow"); + lastprkyevt_time = curtime; + return; + } else if ((curtime - lastprkyevt_time) < POWERKEYEVENT_DEFAULT_LIMIT_MS) { + pr_info("powerkeyevent user press powerkey too fast-time:%d", curtime); + return; + } + lastprkyevt_time = curtime; + zrhung_new_powerkeyevent(curtime); +} + +static int hung_wp_screen_setblank(struct notifier_block *self, unsigned long event, void *data) +{ + unsigned long flags; + struct fb_event *evdata = data; + int blank; + + if (!init_done) + return 0; + + if (event != FB_EVENT_BLANK) + return 0; + + blank = *(int *)evdata->data; + spin_lock_irqsave(&(g_hung_data.lock), flags); + g_hung_data.fb_blank = blank; + if (((g_hung_data.check_id == ZRHUNG_WP_SCREENON) && (blank == 0)) || + ((g_hung_data.check_id == ZRHUNG_WP_SCREENOFF) && (blank != 0))) { + pr_info("check_id=%d, blank=%d", g_hung_data.check_id, g_hung_data.fb_blank); + del_timer(&g_hung_data.timer); + g_hung_data.check_id = ZRHUNG_WP_NONE; + } + spin_unlock_irqrestore(&(g_hung_data.lock), flags); + + return 0; +} + +static void hung_wp_screen_send_work(struct work_struct *work) +{ + unsigned long flags = 0; + + show_state_filter(TASK_UNINTERRUPTIBLE); + + if (g_hung_data.check_id == 1) + zrhung_send_event(WP_SCREEN_DOMAIN, WP_SCREEN_ON_NAME, "none"); + else + zrhung_send_event(WP_SCREEN_DOMAIN, WP_SCREEN_OFF_NAME, "none"); + pr_info("send event: %d\n", g_hung_data.check_id); + spin_lock_irqsave(&(g_hung_data.lock), flags); + g_hung_data.check_id = ZRHUNG_WP_NONE; + spin_unlock_irqrestore(&(g_hung_data.lock), flags); +} + +static void hung_wp_screen_send(struct timer_list *t) +{ + del_timer(&g_hung_data.timer); + pr_info("hung_wp_screen_%d end\n", g_hung_data.tag_id); + queue_work(g_hung_data.workq, &g_hung_data.send_work); +} + +static void hung_wp_screen_start(int check_id) +{ + if (g_hung_data.check_id != ZRHUNG_WP_NONE) { + pr_info("already in check_id: %d\n", g_hung_data.check_id); + return; + } + + g_hung_data.check_id = check_id; + if (timer_pending(&g_hung_data.timer)) + del_timer(&g_hung_data.timer); + + g_hung_data.timer.expires = jiffies + msecs_to_jiffies(DEFAULT_TIMEOUT * TIME_CONVERT_UNIT); + add_timer(&g_hung_data.timer); + pr_info("going to check ID=%d timeout=%d\n", check_id, DEFAULT_TIMEOUT); +} + +void hung_wp_screen_powerkey_ncb(int event) +{ + static int check_off; + unsigned long flags = 0; + + if (!init_done) + return; + + spin_lock_irqsave(&(g_hung_data.lock), flags); + if (event == WP_SCREEN_PWK_PRESS) { + pr_info("hung_wp_screen_%d start! fb_blank=%d", + ++g_hung_data.tag_id, g_hung_data.fb_blank); + check_off = 0; + if (g_hung_data.fb_blank != 0) { + hung_wp_screen_start(ZRHUNG_WP_SCREENON); + } else { + check_off = 1; + pr_info("start longpress test timer\n"); + check_off_point = &check_off; + g_hung_data.long_press_timer.expires = jiffies + + msecs_to_jiffies(LPRESSEVENT_TIME * TIME_CONVERT_UNIT); + if (!timer_pending(&g_hung_data.long_press_timer)) + add_timer(&g_hung_data.long_press_timer); + } + zrhung_powerkeyevent_handler(); + } else if (check_off) { + check_off = 0; + del_timer(&g_hung_data.long_press_timer); + if (event == WP_SCREEN_PWK_RELEASE && g_hung_data.fb_blank == 0) + hung_wp_screen_start(ZRHUNG_WP_SCREENOFF); + } + spin_unlock_irqrestore(&(g_hung_data.lock), flags); +} + +static int __init hung_wp_screen_init(void) +{ + init_done = false; + pr_info("%s start\n", __func__); + g_hung_data.fb_blank = 0; + g_hung_data.tag_id = 0; + g_hung_data.check_id = ZRHUNG_WP_NONE; + spin_lock_init(&(g_hung_data.lock)); + + timer_setup(&g_hung_data.timer, hung_wp_screen_send, 0); + timer_setup(&g_hung_data.long_press_timer, zrhung_wp_lpress_send, 0); + + g_hung_data.workq = create_workqueue("hung_wp_screen_workq"); + if (g_hung_data.workq == NULL) { + pr_err("create workq failed\n"); + return -EFAULT; + } + INIT_WORK(&g_hung_data.send_work, hung_wp_screen_send_work); + INIT_WORK(&powerkeyevent_sendwork, zrhung_powerkeyevent_send_work); + INIT_WORK(&lpressevent_sendwork, zrhung_lpressevent_send_work); + + hung_wp_screen_setblank_ncb.notifier_call = hung_wp_screen_setblank; + fb_register_client(&hung_wp_screen_setblank_ncb); + + init_done = true; + pr_info("%s done\n", __func__); + return 0; +} + +static void __exit hung_wp_screen_exit(void) +{ + fb_unregister_client(&hung_wp_screen_setblank_ncb); + + cancel_work_sync(&lpressevent_sendwork); + cancel_work_sync(&powerkeyevent_sendwork); + cancel_work_sync(&g_hung_data.send_work); + + destroy_workqueue(g_hung_data.workq); + + del_timer_sync(&g_hung_data.timer); + del_timer_sync(&g_hung_data.long_press_timer); +} + +module_init(hung_wp_screen_init); +module_exit(hung_wp_screen_exit); + +MODULE_AUTHOR("OHOS"); +MODULE_DESCRIPTION("Reporting the frozen screen alarm event"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/zerohung/zrhung_event.c b/drivers/staging/zerohung/zrhung_event.c new file mode 100644 index 0000000000000000000000000000000000000000..04b94a77e46ed7f861a2b2f821c966e049a438f1 --- /dev/null +++ b/drivers/staging/zerohung/zrhung_event.c @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 Huawei Technologies Co., Ltd. All rights reserved. + */ + +#define pr_fmt(fmt) "zrhung " fmt + +#include +#include + +#include +#include + +int zrhung_send_event(const char *domain, const char *event_name, const char *msg_buf) +{ + struct hiview_hisysevent *event = NULL; + int ret = 0; + + event = hisysevent_create(domain, event_name, FAULT); + if (!event) { + pr_err("failed to create event"); + return -EINVAL; + } + ret = hisysevent_put_string(event, "MSG", msg_buf); + if (ret != 0) { + pr_err("failed to put sting to event, ret=%d", ret); + goto hisysevent_end; + } + ret = hisysevent_write(event); + +hisysevent_end: + hisysevent_destroy(&event); + return ret; +} diff --git a/include/dfx/hiview_hisysevent.h b/include/dfx/hiview_hisysevent.h new file mode 100644 index 0000000000000000000000000000000000000000..c47d419a2593d29dfb63f010c0e8851833455ed9 --- /dev/null +++ b/include/dfx/hiview_hisysevent.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2022 Huawei Technologies Co., Ltd. All rights reserved. + */ + +#ifndef HIVIEW_HISYSEVENT_H +#define HIVIEW_HISYSEVENT_H + +enum hisysevent_type { + /* fault event */ + FAULT = 1, + + /* statistic event */ + STATISTIC = 2, + + /* security event */ + SECURITY = 3, + + /* behavior event */ + BEHAVIOR = 4 +}; + +struct hiview_hisysevent; + +#ifdef CONFIG_HISYSEVENT + +struct hiview_hisysevent * +hisysevent_create(const char *domain, const char *name, enum hisysevent_type type); +void hisysevent_destroy(struct hiview_hisysevent **event); +int hisysevent_put_integer(struct hiview_hisysevent *event, const char *key, long long value); +int hisysevent_put_string(struct hiview_hisysevent *event, const char *key, const char *value); +int hisysevent_write(struct hiview_hisysevent *event); + +#else + +#include +#include + +static inline struct hiview_hisysevent * +hisysevent_create(const char *domain, const char *name, enum hisysevent_type type) +{ + return NULL; +} + +static inline void hisysevent_destroy(struct hiview_hisysevent **event) +{} + +static inline int +hisysevent_put_integer(struct hiview_hisysevent *event, const char *key, long long value) +{ + return -EOPNOTSUPP; +} + +static inline int +hisysevent_put_string(struct hiview_hisysevent *event, const char *key, const char *value) +{ + return -EOPNOTSUPP; +} + +static inline int hisysevent_write(struct hiview_hisysevent *event) +{ + return -EOPNOTSUPP; +} + +#endif /* CONFIG_HISYSEVENT */ + +#endif /* HIVIEW_HISYSEVENT_H */ diff --git a/include/dfx/hung_wp_screen.h b/include/dfx/hung_wp_screen.h new file mode 100644 index 0000000000000000000000000000000000000000..39bad044c942d47412f68af816528855d9416eed --- /dev/null +++ b/include/dfx/hung_wp_screen.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2022 Huawei Technologies Co., Ltd. All rights reserved. + */ + +#ifndef HUNG_WP_SCREEN_H +#define HUNG_WP_SCREEN_H + +#define WP_SCREEN_PWK_RELEASE 0 +#define WP_SCREEN_PWK_PRESS 1 + +#define ZRHUNG_WP_NONE 0 +#define ZRHUNG_WP_SCREENON 1 +#define ZRHUNG_WP_SCREENOFF 2 + +#define WP_SCREEN_DOMAIN "KERNEL_VENDOR" +#define WP_SCREEN_PWK_NAME "POWER_KEY" +#define WP_SCREEN_LPRESS_NAME "LONG_PRESS" +#define WP_SCREEN_ON_NAME "SCREEN_ON" +#define WP_SCREEN_OFF_NAME "SCREEN_OFF" + +void hung_wp_screen_powerkey_ncb(int event); + +#endif /* HUNG_WP_SCREEN_H */ diff --git a/include/dfx/hungtask_base.h b/include/dfx/hungtask_base.h new file mode 100644 index 0000000000000000000000000000000000000000..b3cf189a0051287f49fea44d3e1e983c912e5778 --- /dev/null +++ b/include/dfx/hungtask_base.h @@ -0,0 +1,111 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2022 Huawei Technologies Co., Ltd. All rights reserved. + */ + +#ifndef DFX_HUNGTASK_BASE_H +#define DFX_HUNGTASK_BASE_H + +#include +#include +#include + +#define ENABLE_SHOW_LEN 8 +#define WHITELIST_STORE_LEN 400 +#define WHITELIST_LEN 61 +#define WHITE_LIST 1 +#define BLACK_LIST 2 +#define HT_ENABLE 1 +#define HT_DISABLE 0 +#define HEARTBEAT_TIME 3 +#define MAX_LOOP_NUM (CONFIG_DEFAULT_HUNG_TASK_TIMEOUT / HEARTBEAT_TIME) +#define ONE_MINUTE (60 / HEARTBEAT_TIME) +#define ONE_AND_HALF_MINUTE (90 / HEARTBEAT_TIME) +#define TWO_MINUTES (120 / HEARTBEAT_TIME) +#define THREE_MINUTES (180 / HEARTBEAT_TIME) +#define TWENTY_SECONDS (21 / HEARTBEAT_TIME) +#define THIRTY_SECONDS (30 / HEARTBEAT_TIME) +#define HUNG_ONE_HOUR (3600 / HEARTBEAT_TIME) +#define HUNG_TEN_MINUTES (600 / HEARTBEAT_TIME) +#define HUNGTASK_REPORT_TIMECOST TWENTY_SECONDS +#define HT_DUMP_IN_PANIC_LOOSE 5 +#define HT_DUMP_IN_PANIC_STRICT 2 +#define REFRESH_INTERVAL THREE_MINUTES +#define FLAG_DUMP_WHITE (1 << 0) +#define FLAG_DUMP_APP (1 << 1) +#define FLAG_DUMP_NOSCHEDULE (1 << 2) +#define FLAG_DUMP_JANK (1 << 3) +#define FLAG_PANIC (1 << 4) +#define FLAG_PF_FROZEN (1 << 6) +#define TASK_TYPE_IGNORE 0 +#define TASK_TYPE_WHITE (1 << 0) +#define TASK_TYPE_APP (1 << 1) +#define TASK_TYPE_JANK (1 << 2) +#define TASK_TYPE_KERNEL (1 << 3) +#define TASK_TYPE_NATIVE (1 << 4) +#define TASK_TYPE_FROZEN (1 << 6) +#define PID_INIT 1 +#define PID_KTHREAD 2 +#define DEFAULT_WHITE_DUMP_CNT MAX_LOOP_NUM +#define DEFAULT_WHITE_PANIC_CNT MAX_LOOP_NUM +#define HUNG_TASK_UPLOAD_ONCE 1 +#define FROZEN_BUF_LEN 1024 +#define MAX_REMOVE_LIST_NUM 200 +#define HUNGTASK_DOMAIN "KERNEL_VENDOR" +#define HUNGTASK_NAME "HUNGTASK" +#define INIT_FREEZE_NAME "INIT_FREEZE" +#define HUNG_TASK_BATCHING 1024 +#define TIME_REFRESH_PIDS 20 +#define PID_ERROR (-1) +#define HUNGTASK_EVENT_WHITELIST 1 +#define REPORT_MSGLENGTH 200 + +struct task_item { + struct rb_node node; + pid_t pid; + pid_t tgid; + char name[TASK_COMM_LEN + 1]; + unsigned long switch_count; + unsigned int task_type; + int dump_wa; + int panic_wa; + int dump_jank; + int d_state_time; + bool isdone_wa; +}; + +struct hashlist_node { + pid_t pid; + struct hlist_node list; +}; + +struct whitelist_item { + pid_t pid; + char name[TASK_COMM_LEN + 1]; +}; + +struct task_hung_upload { + char name[TASK_COMM_LEN + 1]; + pid_t pid; + pid_t tgid; + unsigned int flag; + int duration; +}; + +extern unsigned long sysctl_hung_task_timeout_secs; +extern unsigned int sysctl_hung_task_panic; + +void do_dump_task(struct task_struct *task); +int dump_task_wa(struct task_item *item, int dump_cnt, + struct task_struct *task, unsigned int flag); +void do_show_task(struct task_struct *task, unsigned int flag, int d_state_time); +void hungtask_show_state_filter(unsigned long state_filter); +int htbase_create_sysfs(void); +void htbase_set_panic(int new_did_panic); +void htbase_set_timeout_secs(unsigned long new_hungtask_timeout_secs); +void htbase_check_tasks(unsigned long timeout); +bool hashlist_find(struct hlist_head *head, int count, pid_t tgid); +void hashlist_clear(struct hlist_head *head, int count); +bool hashlist_insert(struct hlist_head *head, int count, pid_t tgid); + +#endif /* DFX_HUNGTASK_BASE_H */ diff --git a/include/dfx/zrhung.h b/include/dfx/zrhung.h new file mode 100644 index 0000000000000000000000000000000000000000..a63462a2885b3d2464c06ed6f5d92e742d3489db --- /dev/null +++ b/include/dfx/zrhung.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2022 Huawei Technologies Co., Ltd. All rights reserved. + */ + +#ifndef ZRHUNG_H +#define ZRHUNG_H + +int zrhung_send_event(const char *domain, const char *event_name, const char *msg_buf); + +#endif /* ZRHUNG_H */