335 lines
11 KiB
C
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_ */ |