#include #include #include #include #include #include #include #include #include #include #include #include #include #include "chipset_common/hwzrhung/zrhung.h" #define CFG_SIZE (64) #define CMD_SIZE (64) #define MAX_CFG_NUM (4) #define MAX_EVENTS (16) #define MAX_WP_BUFSIZE (1024) #define LATENCY_ZRHUNG_LOG_DURATION (60) #define NANOS_PER_MILLISECOND (1000000) #define NANOS_PER_SECOND (NANOS_PER_MILLISECOND * 1000) #define IOWP_WARN(format, args...) \ printk(KERN_WARNING "iowp: " format, ##args) #define IOWP_INFO(format, args...) \ printk(KERN_INFO "iowp: " format, ##args) enum { IOWP_CFG_NOT_READY = 0, IOWP_CFG_READY = 1, IOWP_CFG_INITIALIZED = 2, }; typedef struct iowp_event { pid_t cur_pid; pid_t tgid; char name[BDEVNAME_SIZE]; u64 stamp; /* ms */ } iowp_event_t; typedef struct iowp_config { unsigned int is_ready; unsigned int enabled; unsigned int threshold; u64 period; /* ms */ u64 silence; } iowp_config_t; typedef struct iowp_main { iowp_config_t config; iowp_event_t events[MAX_EVENTS]; unsigned int free; u64 last_report_stamp; } iowp_main_t; typedef struct iowp_work { struct workqueue_struct *workq; struct work_struct config_work; struct work_struct send_work; } iowp_work_t; static char iowp_logbuf[MAX_WP_BUFSIZE]; static iowp_main_t io_wp; static iowp_work_t io_wp_work; static inline unsigned int iowp_config_is_ready(void) { return io_wp.config.is_ready; } static inline unsigned int iowp_config_is_enabled(void) { return io_wp.config.enabled; } static inline u64 iowp_config_get_period(void) { return io_wp.config.period; } static int iowp_parse_config(char *str, uint32_t len, iowp_config_t *cfg_ptr) { int ret = 0; if (str == NULL || len == 0 || cfg_ptr == NULL) return -1; if (MAX_CFG_NUM == sscanf(str, "%d,%llu,%u,%llu", &cfg_ptr->enabled, &cfg_ptr->period, &cfg_ptr->threshold, &cfg_ptr->silence)) { ret = 1; } return ret; } static u8 iowp_config_load(void) { char config_str[CFG_SIZE] = {0}; int ret = 0; iowp_config_t *config = &io_wp.config; ret = zrhung_get_config(ZRHUNG_WP_IO, config_str, sizeof(config_str)); if (ret > 0) { IOWP_WARN("zrhung config not ready, wait...\n"); config->is_ready = IOWP_CFG_NOT_READY; } else if (ret < 0) { IOWP_WARN("zrhung iowp is not enabled\n"); config->is_ready = IOWP_CFG_READY; config->enabled = false; } else { IOWP_INFO("config string(%s) \n", config_str); if(iowp_parse_config(config_str, sizeof(config_str), config) > 0) { config->is_ready = IOWP_CFG_INITIALIZED; } else { config->is_ready = IOWP_CFG_READY; } } IOWP_INFO("ready:%d enable:%d period:%llu threshold:%u silence:%llu\n", config->is_ready, config->enabled, config->period, config->threshold, config->silence); return config->is_ready; } static void iowp_event_ctor(iowp_event_t* this, pid_t pid, pid_t tgid, char* device_name) { #ifdef CONFIG_HISI_TIME this->stamp = hisi_getcurtime() / NANOS_PER_MILLISECOND; #else this->stamp = local_clock() / NANOS_PER_MILLISECOND; #endif /* copy IO latency timeout info */ this->cur_pid = pid; this->tgid = tgid; memcpy(this->name, device_name, strlen(device_name)+1); IOWP_INFO("cur_pid:[%d], tgid[%d], device_name[%s]\n", this->cur_pid, this->tgid,this->name); } static int iowp_event_format(iowp_event_t* this, char *buf, size_t len) { return scnprintf(buf, len, "PID:[%d], TGID[%d], Device_Name[%s] reach the warning throttle \n", this->cur_pid, this->tgid, this->name); } static size_t iowp_format_events(unsigned int pos) { unsigned int s = pos; size_t total_len = sizeof(iowp_logbuf); size_t written_len = 0; for (; s != io_wp.free ; s = (s+1) % MAX_EVENTS) { size_t w_len = iowp_event_format(&io_wp.events[s], iowp_logbuf + written_len, total_len - written_len); written_len += w_len; } IOWP_INFO("written_len: %zu\n", written_len); return written_len; } static void iowp_format_cmd(char* cmd) { // assemble command for zrhung if (cmd != NULL) { #ifdef CONFIG_HISI_TIME u64 cur_stamp = hisi_getcurtime() / NANOS_PER_SECOND; #else u64 cur_stamp = local_clock() / NANOS_PER_SECOND; #endif snprintf(cmd, CMD_SIZE, "d=%d,e=%llu", LATENCY_ZRHUNG_LOG_DURATION, cur_stamp); IOWP_INFO("cur_stamp: %llu \n", cur_stamp); } } static bool iowp_check_threshold(unsigned int *pos) { iowp_config_t * config = &io_wp.config; iowp_event_t * events = io_wp.events; unsigned int last = io_wp.free; unsigned int new = io_wp.free == 0 ? (MAX_EVENTS - 1) : (io_wp.free - 1); unsigned int distance = (last >= config->threshold) ? last-config->threshold : MAX_EVENTS + last-config->threshold; bool need_report = false; if (events[distance].stamp == 0) { return need_report; } if ((events[new].stamp > iowp_config_get_period() + events[distance].stamp)) { IOWP_INFO("need_report: %d \n", need_report); return need_report; } need_report = true; if (pos) *pos = distance; return need_report; } static iowp_event_t* iowp_get_slot(void) { iowp_event_t* ptr = io_wp.events + io_wp.free; io_wp.free++; io_wp.free = io_wp.free % MAX_EVENTS; IOWP_INFO("free: %d \n", io_wp.free); return ptr; } /*=========================================================================== ** Function : iowp_event_config_work * @brief * Send envent through zrhung_send_event ** ==========================================================================*/ void iowp_event_config_work(struct work_struct *work) { IOWP_WARN("%s: send event: %d\n",__func__,ZRHUNG_WP_IO); if (IOWP_CFG_INITIALIZED == iowp_config_load()) { IOWP_INFO("Config Load successfully!\n"); } } /*=========================================================================== ** Function : iowp_event_send_work * @brief * Send envent through zrhung_send_event ** ==========================================================================*/ void iowp_event_send_work(struct work_struct *work) { char zrhung_cmd[CMD_SIZE] = {'\0'}; IOWP_INFO("%s: send event: %d\n",__func__,ZRHUNG_WP_IO); iowp_format_cmd(zrhung_cmd); IOWP_INFO("%s: cmd_buf: %s\n", __func__, zrhung_cmd); zrhung_send_event(ZRHUNG_WP_IO, zrhung_cmd, iowp_logbuf); IOWP_INFO("%s: send event buffer: %s\n", __func__, iowp_logbuf); } /*=========================================================================== ** Function : iowp_init * @brief * io watchponit init ZeroHung config in blk-core ** ==========================================================================*/ void iowp_init(void) { IOWP_INFO("%s: %d\n",__func__,iowp_config_is_ready()); if (iowp_config_is_ready() > IOWP_CFG_NOT_READY) { return; } memset(&io_wp, 0, sizeof(iowp_main_t)); queue_work(io_wp_work.workq, &io_wp_work.config_work); } int get_fg_pid(void); /*=========================================================================== ** Function : iowp_event_config_work * @brief * io watchponit init ZeroHung workqueque in blk-core ** ==========================================================================*/ void iowp_workqueue_init(void) { io_wp_work.workq = create_workqueue("io_latency_workq"); if (NULL == io_wp_work.workq) { IOWP_WARN("%s: create workq failed!\n", __func__); return; } INIT_WORK(&io_wp_work.config_work, iowp_event_config_work); INIT_WORK(&io_wp_work.send_work, iowp_event_send_work); } /*=========================================================================== ** Function : iowp_event_config_work * @brief * io watchponit report IO latency timeout to ZeroHung ** ==========================================================================*/ void iowp_report(pid_t pid , pid_t tgid, char* name) { unsigned int pos = 0; iowp_event_t* cur_event = NULL; /*initialize config*/ iowp_init(); /*check if foreground process*/ if (pid == get_fg_pid()) { return; } /* check if iowp config is ready */ if (IOWP_CFG_NOT_READY == iowp_config_is_ready()) { IOWP_WARN("iowp config is not ready ,exit\n"); return; } /* check if iowp is enabled */ if (!iowp_config_is_enabled()) { IOWP_WARN("iowp disabled ,exit\n"); return; } /* get free slot event */ cur_event = iowp_get_slot(); iowp_event_ctor(cur_event, pid, tgid, name); if (cur_event->stamp < io_wp.last_report_stamp + io_wp.config.silence) { IOWP_WARN("curr(%llu) less then last report(%llu) + silence(%llu) \n", cur_event->stamp, io_wp.last_report_stamp, io_wp.config.silence); return; } /* check if need to report events */ if (true == iowp_check_threshold(&pos)) { size_t buf_len = iowp_format_events(pos); io_wp.last_report_stamp = cur_event->stamp; queue_work(io_wp_work.workq, &io_wp_work.send_work); IOWP_WARN("buffer length (%zu)\n", buf_len); } return; }