Files

335 lines
11 KiB
C

// SPDX-License-Identifier: GPL-2.0
/**
* Copyright (c) 2024-2025 CALCULET Technologies Ltd. All rights reserved.
*/
#ifndef _CALCULET_UTILS_H_
#define _CALCULET_UTILS_H_
#include <linux/version.h>
#include <linux/kern_levels.h>
#include <linux/scatterlist.h>
#include <linux/vmalloc.h>
#include <linux/time.h>
#include <linux/ktime.h>
#include <linux/delay.h>
#include <asm/delay.h>
#include <asm/div64.h>
// 日志级别定义
#define CALCULET_LOG_EMERG 0
#define CALCULET_LOG_ALERT 1
#define CALCULET_LOG_CRIT 2
#define CALCULET_LOG_ERROR 3
#define CALCULET_LOG_WARN 4
#define CALCULET_LOG_NOTICE 5
#define CALCULET_LOG_INFO 6
#define CALCULET_LOG_DEBUG 7
// 编译时日志级别控制(通过Makefile设置)
#ifndef CALCULET_LOG_LEVEL
#ifdef DEBUG
#define CALCULET_LOG_LEVEL CALCULET_LOG_DEBUG
#else
#define CALCULET_LOG_LEVEL CALCULET_LOG_INFO
#endif
#endif
// 统一的日志函数声明
extern int calculet_log_level;
// 主要的日志宏
#define calculet_log(level, fmt, ...) \
do { \
if ((level) <= CALCULET_LOG_LEVEL && (level) <= calculet_log_level) { \
const char *level_str; \
switch (level) { \
case CALCULET_LOG_EMERG: level_str = KERN_EMERG; break; \
case CALCULET_LOG_ALERT: level_str = KERN_ALERT; break; \
case CALCULET_LOG_CRIT: level_str = KERN_CRIT; break; \
case CALCULET_LOG_ERROR: level_str = KERN_ERR; break; \
case CALCULET_LOG_WARN: level_str = KERN_WARNING; break; \
case CALCULET_LOG_NOTICE: level_str = KERN_NOTICE; break; \
case CALCULET_LOG_INFO: level_str = KERN_INFO; break; \
case CALCULET_LOG_DEBUG: level_str = KERN_DEBUG; break; \
default: level_str = KERN_INFO; break; \
} \
printk("%s" "calculet: " fmt "\n", level_str, ##__VA_ARGS__); \
} \
} while (0)
// 带板卡信息的日志宏
#define calculet_board_log(level, board, fmt, ...) \
do { \
if ((level) <= CALCULET_LOG_LEVEL && (level) <= calculet_log_level && (board)) { \
const char *level_str; \
switch (level) { \
case CALCULET_LOG_EMERG: level_str = KERN_EMERG; break; \
case CALCULET_LOG_ALERT: level_str = KERN_ALERT; break; \
case CALCULET_LOG_CRIT: level_str = KERN_CRIT; break; \
case CALCULET_LOG_ERROR: level_str = KERN_ERR; break; \
case CALCULET_LOG_WARN: level_str = KERN_WARNING; break; \
case CALCULET_LOG_NOTICE: level_str = KERN_NOTICE; break; \
case CALCULET_LOG_INFO: level_str = KERN_INFO; break; \
case CALCULET_LOG_DEBUG: level_str = KERN_DEBUG; break; \
default: level_str = KERN_INFO; break; \
} \
if (board && (board)->pDev && &(board)->pDev->dev) { \
dev_printk(level_str, &(board)->pDev->dev, fmt, ##__VA_ARGS__); \
} else { \
printk("%s" "calculet: [board error] " fmt "\n", level_str, ##__VA_ARGS__); \
} \
} \
} while (0)
// 带设备信息的日志宏
#define calculet_dev_log(level, dev, fmt, ...) \
do { \
if ((level) <= CALCULET_LOG_LEVEL && (level) <= calculet_log_level && (dev)) { \
const char *level_str; \
switch (level) { \
case CALCULET_LOG_EMERG: level_str = KERN_EMERG; break; \
case CALCULET_LOG_ALERT: level_str = KERN_ALERT; break; \
case CALCULET_LOG_CRIT: level_str = KERN_CRIT; break; \
case CALCULET_LOG_ERROR: level_str = KERN_ERR; break; \
case CALCULET_LOG_WARN: level_str = KERN_WARNING; break; \
case CALCULET_LOG_NOTICE: level_str = KERN_NOTICE; break; \
case CALCULET_LOG_INFO: level_str = KERN_INFO; break; \
case CALCULET_LOG_DEBUG: level_str = KERN_DEBUG; break; \
default: level_str = KERN_INFO; break; \
} \
if (dev) { \
dev_printk(level_str, dev, fmt, ##__VA_ARGS__); \
} else { \
printk("%s" "calculet: [dev error] " fmt "\n", level_str, ##__VA_ARGS__); \
} \
} \
} while (0)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0)
#define class_create_compat class_create
#elif defined(RHEL_MAJOR) && RHEL_MAJOR >= 9
#define class_create_compat class_create
#else
#define class_create_compat(name) class_create(THIS_MODULE, name)
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0)
#define pci_printk(level, pdev, fmt, arg...) \
dev_printk(level, &(pdev)->dev, fmt, ##arg)
#define pci_emerg(pdev, fmt, arg...) dev_emerg(&(pdev)->dev, fmt, ##arg)
#define pci_alert(pdev, fmt, arg...) dev_alert(&(pdev)->dev, fmt, ##arg)
#define pci_crit(pdev, fmt, arg...) dev_crit(&(pdev)->dev, fmt, ##arg)
#define pci_err(pdev, fmt, arg...) dev_err(&(pdev)->dev, fmt, ##arg)
#define pci_warn(pdev, fmt, arg...) dev_warn(&(pdev)->dev, fmt, ##arg)
#define pci_notice(pdev, fmt, arg...) dev_notice(&(pdev)->dev, fmt, ##arg)
#define pci_info(pdev, fmt, arg...) dev_info(&(pdev)->dev, fmt, ##arg)
#define pci_dbg(pdev, fmt, arg...) dev_dbg(&(pdev)->dev, fmt, ##arg)
#endif
// 性能测量宏(简化)
#ifdef CALCULET_PERF_ANALYSIS
#define CALCULET_PERF_START(name) \
ktime_t __perf_start_##name = ktime_get()
#define CALCULET_PERF_END(name, fmt, ...) \
do { \
ktime_t __perf_end_##name = ktime_get(); \
s64 __perf_delta_##name = ktime_to_ns(ktime_sub(__perf_end_##name, __perf_start_##name)); \
calculet_log(CALCULET_LOG_DEBUG, "PERF %s: %lld ns - " fmt, #name, __perf_delta_##name, ##__VA_ARGS__); \
} while (0)
#else
#define CALCULET_PERF_START(name) do { } while (0)
#define CALCULET_PERF_END(name, fmt, ...) do { } while (0)
#endif
// 调试断言宏
#ifdef CALCULET_DEBUG_MODE
#define CALCULET_ASSERT(cond) \
do { \
if (unlikely(!(cond))) { \
calculet_log(CALCULET_LOG_CRIT, "ASSERTION FAILED: %s at %s:%d", #cond, __FILE__, __LINE__); \
dump_stack(); \
} \
} while (0)
#define CALCULET_ASSERT_MSG(cond, fmt, ...) \
do { \
if (unlikely(!(cond))) { \
calculet_log(CALCULET_LOG_CRIT, "ASSERTION FAILED: %s at %s:%d - " fmt, \
#cond, __FILE__, __LINE__, ##__VA_ARGS__); \
dump_stack(); \
} \
} while (0)
#else
#define CALCULET_ASSERT(cond) do { } while (0)
#define CALCULET_ASSERT_MSG(cond, fmt, ...) do { } while (0)
#endif
// 更多兼容性定义
#if LINUX_VERSION_CODE >= KERNEL_VERSION( 5, 0, 0 )
#define compatible_access_ok(a,b,c) access_ok(b, c)
#else
#define compatible_access_ok(a,b,c) access_ok(a, b, c)
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 5, 0)
#define get_user_pages_compact get_user_pages
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)
#define get_user_pages_compact(start, nr_pages, gup_flags, pages) \
get_user_pages(start, nr_pages, gup_flags, pages, NULL)
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 168)) && (LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0))
#define get_user_pages_compact(start, nr_pages, gup_flags, pages) \
get_user_pages(current, current->mm, start, nr_pages, gup_flags, pages, NULL)
#else
static inline int get_user_pages_compact(struct task_struct *current,
struct mm_struct *mm,
unsigned long start,
int nr_pages,
int write,
int force,
struct page **pages,
struct vm_area_struct **vmas)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,6,0)
return get_user_pages(start, nr_pages, write ? FOLL_WRITE : 0, pages, vmas);
#else
return get_user_pages(current, mm, start, nr_pages, write, force, pages, vmas);
#endif
}
#endif
#ifndef _LINUX_MMAP_LOCK_H
static inline void mmap_read_lock(struct mm_struct *mm)
{
down_read(&mm->mmap_sem);
}
static inline void mmap_read_unlock(struct mm_struct *mm)
{
up_read(&mm->mmap_sem);
}
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION( 4, 13, 0 )
#define wait_queue_t wait_queue_entry_t
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION( 4, 15, 0 )
#define ACCESS_ONCE READ_ONCE
#endif
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22))
#define CALCULET_IRQ_FLAGS (SA_SHIRQ | SA_INTERRUPT)
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) && LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0))
#define CALCULET_IRQ_FLAGS (IRQF_SHARED | IRQF_DISABLED)
#else
#define CALCULET_IRQ_FLAGS (IRQF_SHARED)
#endif
// 实用工具宏
#define CALCULET_ALIGN_UP(value, align) \
(((value) + (align) - 1) & ~((align) - 1))
#define CALCULET_ALIGN_DOWN(value, align) \
((value) & ~((align) - 1))
#define CALCULET_DIV_ROUND_UP(n, d) \
(((n) + (d) - 1) / (d))
#define CALCULET_IS_POWER_OF_2(x) \
((x) != 0 && (((x) & ((x) - 1)) == 0))
// 错误处理工具
#define CALCULET_RETURN_IF_NULL(ptr, ret) \
do { if (unlikely(!(ptr))) return (ret); } while (0)
#define CALCULET_RETURN_IF_ERROR(expr) \
do { \
int __ret = (expr); \
if (unlikely(__ret < 0)) return __ret; \
} while (0)
// 时间转换工具
static inline u64 calculet_ktime_to_us(ktime_t kt)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0)
return ktime_divns(kt, NSEC_PER_USEC);
#else
u64 ns = ktime_to_ns(kt);
do_div(ns, NSEC_PER_USEC);
return ns;
#endif
}
static inline u64 calculet_ktime_to_ms(ktime_t kt)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0)
return ktime_divns(kt, NSEC_PER_MSEC);
#else
u64 ns = ktime_to_ns(kt);
do_div(ns, NSEC_PER_MSEC);
return ns;
#endif
}
// 延迟和等待工具
static inline void calculet_udelay_range(unsigned long min_us, unsigned long max_us)
{
if (min_us == max_us) {
udelay(min_us);
} else {
usleep_range(min_us, max_us);
}
}
// 统计工具(简化的原子版本)
struct calculet_stats_counter {
atomic64_t count;
atomic64_t total;
atomic64_t min;
atomic64_t max;
};
static inline void calculet_stats_init(struct calculet_stats_counter *stats)
{
atomic64_set(&stats->count, 0);
atomic64_set(&stats->total, 0);
atomic64_set(&stats->min, LLONG_MAX);
atomic64_set(&stats->max, 0);
}
static inline void calculet_stats_update(struct calculet_stats_counter *stats, s64 value)
{
s64 old_min, old_max;
atomic64_inc(&stats->count);
atomic64_add(value, &stats->total);
// 使用简单的CAS来更新min/max
do {
old_min = atomic64_read(&stats->min);
} while (value < old_min && atomic64_cmpxchg(&stats->min, old_min, value) != old_min);
do {
old_max = atomic64_read(&stats->max);
} while (value > old_max && atomic64_cmpxchg(&stats->max, old_max, value) != old_max);
}
static inline s64 calculet_stats_avg(struct calculet_stats_counter *stats)
{
s64 count = atomic64_read(&stats->count);
return count ? atomic64_read(&stats->total) / count : 0;
}
// 函数声明
int calculet_utils_init(void);
void calculet_utils_cleanup(void);
void calculet_global_stats_init(void);
void calculet_global_stats_update(s64 value);
void calculet_global_stats_get(s64 *count, s64 *total, s64 *min, s64 *max, s64 *avg);
void calculet_global_stats_cleanup(void);
void calculet_random_delay_us(unsigned int min_us, unsigned int max_us);
#endif /* _CALCULET_UTILS_H_ */