data: publish complete Calculet NPU research archive

This commit is contained in:
2026-08-02 14:57:11 +08:00
parent b5c4fbffab
commit 98e6cadcb6
12400 changed files with 6166642 additions and 0 deletions
@@ -0,0 +1,22 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: cal-pcie
Source: http://192.168.20.33:1000/niuzhenyu/cal-pcie.git
Files: debian/*
Copyright: 2026 Junwei Qin <qinjunwei@calculet.tech>
License: GPL-2+
This package is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
.
This package 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.
.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>
.
On Debian systems, the complete text of the GNU General
Public License version 2 can be found in "/usr/share/common-licenses/GPL-2".
@@ -0,0 +1,334 @@
# SPDX-License-Identifier: GPL-2.0
# quiet command
ifeq ($(Q),)
Q=@
endif
# If KERNELRELEASE is defined, we've been invoked from the
# kernel build system and can use its language.
ifneq ($(KERNELRELEASE),)
# 主要的构建目标
obj-m := calculet_pci.o
# 源文件列表 - 包含所有优化后的模块
calculet_pci-objs := calculet_pci_core.o board_mgr.o proc.o dma.o fops.o utils.o
# 基本编译标志
ccflags-y += -DCALCULET_DRIVER_OPTIMIZED
ccflags-y += -Wall -Wextra -Wno-unused-parameter
ccflags-y += -DCALCULET_VERSION_MAJOR=1 -DCALCULET_VERSION_MINOR=1
# 日志级别控制
# LOG_LEVEL: 0=EMERG, 1=ALERT, 2=CRIT, 3=ERROR, 4=WARN, 5=NOTICE, 6=INFO, 7=DEBUG
ifndef LOG_LEVEL
ifeq ($(DEBUG), 1)
LOG_LEVEL = 7 # DEBUG级别
else
LOG_LEVEL = 6 # INFO级别
endif
endif
ccflags-y += -DCALCULET_LOG_LEVEL=$(LOG_LEVEL)
# 调试模式设置
ifeq ($(DEBUG), 1)
ccflags-y += -DDEBUG -g -O0
ccflags-y += -DCALCULET_DEBUG_MODE
ccflags-y += -DCALCULET_LOG_LEVEL=7
else
ccflags-y += -O2
ccflags-y += -DNDEBUG
endif
# 性能分析模式
ifeq ($(PERF), 1)
ccflags-y += -DCALCULET_PERF_ANALYSIS
endif
# 模拟器模式
ifeq ($(EMULATOR), 1)
ccflags-y += -DCALCULET_EMULATOR
endif
# 启用多线程优化
ccflags-y += -DCALCULET_MULTITHREAD_OPTIMIZED
# 严格模式(更多编译检查)
ifeq ($(STRICT), 1)
ccflags-y += -Werror -Wno-error=unused-function -Wno-error=unused-variable
endif
# 内存调试
ifeq ($(MEM_DEBUG), 1)
ccflags-y += -DCALCULET_MEM_DEBUG
endif
else
# Otherwise we were called directly from the command
# line; invoke the kernel build system.
BUILD_DIR = build
# default is system arch
ifndef ARCH
ARCH=$(shell uname -m)
endif
# set output folder ( e.g. release | debug )
# Default is release folder
TARGET_DIR:=$(BUILD_DIR)/release/$(ARCH)
DRIVER_NAME=calculet_pci.ko
DRIVER_NAME_NO_EXT=calculet_pci
# 版本号提取 - 使用脚本避免shell语法问题
DRIVER_VERSION=$(shell ./extract_version.sh 2>/dev/null || echo "1.0.0")
# 日志级别控制
ifndef LOG_LEVEL
ifeq ($(DEBUG), 1)
LOG_LEVEL = 7 # DEBUG级别
else
LOG_LEVEL = 6 # INFO级别
endif
endif
ifeq ($(DEBUG), 1)
GDB_FLAG=CONFIG_DEBUG_INFO=y CONFIG_FRAME_POINTER=y
TARGET_DIR:=$(BUILD_DIR)/debug/$(ARCH)
endif
# 构建参数设置
USER_FLAGS=
# Internal use only. Compile driver in emulator mode.
ifeq ($(EMULATOR), 1)
USER_FLAGS+=EMULATOR=1
endif
# 性能分析模式
ifeq ($(PERF), 1)
USER_FLAGS+= PERF=1
endif
# 严格模式
ifeq ($(STRICT), 1)
USER_FLAGS+= STRICT=1
endif
# 内存调试模式
ifeq ($(MEM_DEBUG), 1)
USER_FLAGS+= MEM_DEBUG=1
endif
# 日志级别传递
USER_FLAGS+= LOG_LEVEL=$(LOG_LEVEL)
ifndef kernelver
kernelver=$(shell uname -r)
endif
MODULES := /lib/modules/${kernelver}/
KERNEL_DIR ?= $(MODULES)/build
DEPMOD ?= depmod
PWD := $(shell pwd)
default: all
help:
$(Q)echo "******************************************************************************"
$(Q)echo "* Optimized PCIe Driver *"
$(Q)echo "* usage: make [options] [target] *"
$(Q)echo "* *"
$(Q)echo "* options: *"
$(Q)echo "* DEBUG=1 : Activate debug mode with full debugging support *"
$(Q)echo "* LOG_LEVEL=n : Set log level (0=EMERG to 7=DEBUG, default: 6 for *"
$(Q)echo "* release, 7 for debug) *"
$(Q)echo "* STRICT=1 : Enable strict compilation with -Werror *"
$(Q)echo "* PERF=1 : Enable performance analysis features *"
$(Q)echo "* MEM_DEBUG=1 : Enable memory debugging and leak detection *"
$(Q)echo "* EMULATOR=1 : Compile driver in emulator mode *"
$(Q)echo "* Q= : Activate makefile verbose mode *"
$(Q)echo "* *"
$(Q)echo "* target: *"
$(Q)echo "* all Generate the ko file in $(BUILD_DIR)/[release|debug]/$(ARCH) *"
$(Q)echo "* clean Delete generated files and $(BUILD_DIR) directory *"
$(Q)echo "* install Install driver and setup auto boot *"
$(Q)echo "* install_dkms Install driver using DKMS *"
$(Q)echo "* uninstall Uninstall driver *"
$(Q)echo "* check Run static analysis and code checks *"
$(Q)echo "* test Run basic driver tests (if available) *"
$(Q)echo "* debug Build debug version (same as DEBUG=1 all) *"
$(Q)echo "* release Build release version (optimized) *"
$(Q)echo "* help Display this help *"
$(Q)echo "* *"
$(Q)echo "* Examples: *"
$(Q)echo "* make # Build release version *"
$(Q)echo "* make DEBUG=1 # Build debug version *"
$(Q)echo "* make LOG_LEVEL=4 # Build with WARN log level *"
$(Q)echo "* make PERF=1 STRICT=1 # Build with performance analysis *"
$(Q)echo "******************************************************************************"
all: $(TARGET_DIR)
$(Q)echo "Building optimized PCIe driver (LOG_LEVEL=$(LOG_LEVEL))..."
$(Q)$(MAKE) -C $(KERNEL_DIR) M=$(PWD) $(GDB_FLAG) $(USER_FLAGS) modules
$(Q)cp $(DRIVER_NAME) $(TARGET_DIR)
$(Q)echo "Build completed. Driver located at: $(TARGET_DIR)/$(DRIVER_NAME)"
debug:
$(Q)echo "Building debug version..."
$(Q)$(MAKE) DEBUG=1 all
release:
$(Q)echo "Building release version..."
$(Q)$(MAKE) DEBUG=0 all
$(TARGET_DIR):
$(Q)mkdir -p $@
clean:
$(Q)echo "Cleaning build artifacts..."
$(Q)$(MAKE) -C $(KERNEL_DIR) M=$(PWD) clean
$(Q)rm -rf $(BUILD_DIR)
$(Q)rm -f *.o.ur-safe
$(Q)echo "Clean completed."
install:
$(Q)echo "Installing driver..."
$(Q)$(MAKE) -C $(KERNEL_DIR) M=$(PWD) INSTALL_MOD_DIR=kernel/drivers/misc modules_install
$(Q)$(DEPMOD) -a
$(Q)echo "Driver installed successfully."
uninstall: uninstall_all_dkms
ifneq ($(wildcard $(MODULES)),)
$(Q)echo "Uninstalling driver..."
$(Q)rm -f $(MODULES)kernel/drivers/misc/$(DRIVER_NAME)
$(Q)$(DEPMOD) -a
$(Q)echo "Driver uninstalled."
endif
install_dkms: uninstall
ifneq ($(shell id -u),0)
@echo "make install_dkms should run as root"
exit 1
endif
ifeq ($(strip $(shell which dkms)),)
@echo "make install_dkms requires dkms to be installed"
exit 1
endif
$(Q)echo "Building DKMS package..."
# build DKMS
$(Q)mkdir -p /usr/src/$(DRIVER_NAME_NO_EXT)-$(DRIVER_VERSION)
$(Q)cp -r . /usr/src/$(DRIVER_NAME_NO_EXT)-$(DRIVER_VERSION)/
$(Q)echo 'PACKAGE_NAME="$(DRIVER_NAME_NO_EXT)"' > /usr/src/$(DRIVER_NAME_NO_EXT)-$(DRIVER_VERSION)/dkms.conf
$(Q)echo 'PACKAGE_VERSION="$(DRIVER_VERSION)"' >> /usr/src/$(DRIVER_NAME_NO_EXT)-$(DRIVER_VERSION)/dkms.conf
$(Q)echo 'BUILT_MODULE_NAME[0]="$(DRIVER_NAME_NO_EXT)"' >> /usr/src/$(DRIVER_NAME_NO_EXT)-$(DRIVER_VERSION)/dkms.conf
$(Q)echo 'DEST_MODULE_LOCATION[0]="/kernel/drivers/misc"' >> /usr/src/$(DRIVER_NAME_NO_EXT)-$(DRIVER_VERSION)/dkms.conf
$(Q)echo 'MAKE[0]="make all"' >> /usr/src/$(DRIVER_NAME_NO_EXT)-$(DRIVER_VERSION)/dkms.conf
$(Q)echo 'CLEAN="make clean"' >> /usr/src/$(DRIVER_NAME_NO_EXT)-$(DRIVER_VERSION)/dkms.conf
$(Q)echo 'AUTOINSTALL="yes"' >> /usr/src/$(DRIVER_NAME_NO_EXT)-$(DRIVER_VERSION)/dkms.conf
$(Q)dkms add -m $(DRIVER_NAME_NO_EXT) -v $(DRIVER_VERSION)
$(Q)dkms build -m $(DRIVER_NAME_NO_EXT) -v $(DRIVER_VERSION) || (cat /var/lib/dkms/$(DRIVER_NAME_NO_EXT)/$(DRIVER_VERSION)/build/make.log; exit 1)
# install DKMS
$(Q)dkms install -m $(DRIVER_NAME_NO_EXT) -v $(DRIVER_VERSION) --force
$(Q)echo "DKMS installation completed."
uninstall_all_dkms:
ifneq ($(shell id -u),0)
@echo "make uninstall_all_dkms should run as root"
exit 1
endif
# Uninstall driver from dkms, if dkms is installed
# If the driver wasn't installed with dkms, the following commands won't do anything
ifneq ($(strip $(shell which dkms)),)
-$(Q)dkms remove $(DRIVER_NAME_NO_EXT)/$(DRIVER_VERSION) --all 2>/dev/null || true
-$(Q)rm -rf /usr/src/$(DRIVER_NAME_NO_EXT)-$(DRIVER_VERSION) 2>/dev/null || true
$(Q)echo "DKMS uninstallation completed."
endif
# 代码检查目标
check:
$(Q)echo "Running code checks..."
$(Q)if command -v sparse >/dev/null 2>&1; then \
echo "Running sparse analysis..."; \
$(MAKE) -C $(KERNEL_DIR) M=$(PWD) C=1 modules; \
else \
echo "Sparse not found, skipping static analysis"; \
fi
$(Q)if command -v checkpatch.pl >/dev/null 2>&1; then \
echo "Running checkpatch analysis..."; \
find . -name "*.c" -o -name "*.h" | xargs -I {} checkpatch.pl --no-tree --file {}; \
else \
echo "checkpatch.pl not found, skipping patch style check"; \
fi
$(Q)echo "Code checks completed."
# 测试目标
test: all
$(Q)echo "Running basic driver tests..."
$(Q)if [ -f tests/run_tests.sh ]; then \
cd tests && ./run_tests.sh; \
else \
echo "No tests available. Please implement tests/run_tests.sh"; \
fi
# 性能分析目标
perf:
$(Q)echo "Building driver with performance analysis..."
$(Q)$(MAKE) PERF=1 all
# 文档生成
docs:
$(Q)echo "Generating documentation..."
$(Q)if command -v doxygen >/dev/null 2>&1; then \
doxygen Doxyfile 2>/dev/null || echo "Doxyfile not found"; \
else \
echo "Doxygen not found, skipping documentation generation"; \
fi
# 打包目标
package: all
$(Q)echo "Creating driver package..."
$(Q)mkdir -p package/calculet-driver-$(DRIVER_VERSION)
$(Q)cp -r . package/calculet-driver-$(DRIVER_VERSION)/
$(Q)cd package && tar czf calculet-driver-$(DRIVER_VERSION).tar.gz calculet-driver-$(DRIVER_VERSION)/
$(Q)echo "Package created: package/calculet-driver-$(DRIVER_VERSION).tar.gz"
# 显示编译信息
info:
$(Q)echo "Driver Information:"
$(Q)echo " Name: $(DRIVER_NAME)"
$(Q)echo " Version: $(DRIVER_VERSION)"
$(Q)echo " Architecture: $(ARCH)"
$(Q)echo " Kernel: $(kernelver)"
$(Q)echo " Build Dir: $(TARGET_DIR)"
$(Q)echo " Log Level: $(LOG_LEVEL)"
$(Q)echo " Debug Mode: $(if $(DEBUG),Enabled,Disabled)"
$(Q)echo " Emulator: $(if $(EMULATOR),Enabled,Disabled)"
$(Q)echo " Performance: $(if $(PERF),Enabled,Disabled)"
$(Q)echo " Strict Mode: $(if $(STRICT),Enabled,Disabled)"
$(Q)echo " Memory Debug: $(if $(MEM_DEBUG),Enabled,Disabled)"
# 日志级别说明
log-help:
$(Q)echo "Log Level Settings:"
$(Q)echo " 0 = EMERG - Emergency: system is unusable"
$(Q)echo " 1 = ALERT - Alert: action must be taken immediately"
$(Q)echo " 2 = CRIT - Critical: critical conditions"
$(Q)echo " 3 = ERROR - Error: error conditions"
$(Q)echo " 4 = WARN - Warning: warning conditions"
$(Q)echo " 5 = NOTICE - Notice: normal but significant condition"
$(Q)echo " 6 = INFO - Info: informational messages (default for release)"
$(Q)echo " 7 = DEBUG - Debug: debug-level messages (default for debug)"
$(Q)echo ""
$(Q)echo "Examples:"
$(Q)echo " make LOG_LEVEL=3 # Only show errors and above"
$(Q)echo " make LOG_LEVEL=7 # Show all messages"
$(Q)echo " make DEBUG=1 # Debug build with LOG_LEVEL=7"
endif
.PHONY: help all clean install uninstall install_dkms uninstall_all_dkms check test perf docs package info debug release log-help
@@ -0,0 +1,381 @@
// SPDX-License-Identifier: GPL-2.0
/**
* Copyright (c) 2024-2025 CALCULET Technologies Ltd. All rights reserved.
*/
#include "board_mgr.h"
#include "proc.h"
#include "utils.h"
#include <linux/idr.h>
static struct calculet_board_manager g_board_mgr = {
.board_list = LIST_HEAD_INIT(g_board_mgr.board_list),
.board_mutex = __MUTEX_INITIALIZER(g_board_mgr.board_mutex),
.board_count = ATOMIC_INIT(0),
.next_board_index = 0,
.index_lock = __SPIN_LOCK_UNLOCKED(g_board_mgr.index_lock),
.initialized = false,
};
// 板卡引用计数管理
void calculet_board_get(struct calculet_pcie_board *board)
{
if (board) {
kref_get(&board->ref_count);
}
}
void calculet_board_put(struct calculet_pcie_board *board)
{
if (board) {
kref_put(&board->ref_count, calculet_board_release);
}
}
void calculet_board_release(struct kref *ref)
{
struct calculet_pcie_board *board = container_of(ref, struct calculet_pcie_board, ref_count);
calculet_log(CALCULET_LOG_DEBUG, "Releasing board %d resources", board->board_index);
// 释放IDR索引
spin_lock(&g_board_mgr.index_lock);
idr_remove(&g_board_mgr.board_idr, board->board_index);
spin_unlock(&g_board_mgr.index_lock);
kfree(board);
}
// 板卡状态管理(简化,去掉不必要的锁)
void calculet_board_set_state(struct calculet_pcie_board *board, enum calculet_board_state state)
{
if (board && board->state != state) {
calculet_log(CALCULET_LOG_DEBUG, "Board %d state change: %d -> %d",
board->board_index, board->state, state);
WRITE_ONCE(board->state, state);
}
}
enum calculet_board_state calculet_board_get_state(struct calculet_pcie_board *board)
{
return board ? READ_ONCE(board->state) : BOARD_STATE_UNKNOWN;
}
bool calculet_board_is_operational(struct calculet_pcie_board *board)
{
if (!board) return false;
enum calculet_board_state state = calculet_board_get_state(board);
return (state == BOARD_STATE_ACTIVE) && board->pcie_resources.resources_valid;
}
int calculet_board_mgr_init(void)
{
if (g_board_mgr.initialized) {
return 0;
}
INIT_LIST_HEAD(&g_board_mgr.board_list);
mutex_init(&g_board_mgr.board_mutex);
atomic_set(&g_board_mgr.board_count, 0);
g_board_mgr.next_board_index = 0;
spin_lock_init(&g_board_mgr.index_lock);
// 初始化IDR
idr_init(&g_board_mgr.board_idr);
// 初始化全局复位控制
atomic_set(&g_board_mgr.reset_ctrl.boards_resetting, 0);
atomic_set(&g_board_mgr.reset_ctrl.boards_total, 0);
mutex_init(&g_board_mgr.reset_ctrl.reset_mutex);
init_waitqueue_head(&g_board_mgr.reset_ctrl.reset_wq);
g_board_mgr.initialized = true;
calculet_log(CALCULET_LOG_DEBUG, "Board manager initialized");
return 0;
}
void calculet_board_mgr_cleanup(void)
{
struct calculet_pcie_board *board, *tmp;
if (!g_board_mgr.initialized) {
return;
}
mutex_lock(&g_board_mgr.board_mutex);
list_for_each_entry_safe(board, tmp, &g_board_mgr.board_list, board_list) {
list_del(&board->board_list);
calculet_board_set_state(board, BOARD_STATE_REMOVING);
calculet_board_put(board);
}
mutex_unlock(&g_board_mgr.board_mutex);
// 销毁IDR
idr_destroy(&g_board_mgr.board_idr);
g_board_mgr.initialized = false;
calculet_log(CALCULET_LOG_DEBUG, "Board manager cleaned up");
}
struct calculet_pcie_board* calculet_board_mgr_get_board(u32 index)
{
struct calculet_pcie_board *board = NULL;
if (!g_board_mgr.initialized) {
return NULL;
}
rcu_read_lock();
board = idr_find(&g_board_mgr.board_idr, index);
if (board && calculet_board_is_operational(board)) {
calculet_board_get(board);
} else {
board = NULL;
}
rcu_read_unlock();
return board;
}
void calculet_board_mgr_put_board(struct calculet_pcie_board *board)
{
calculet_board_put(board);
}
int calculet_board_mgr_add_board(struct calculet_pcie_board *board)
{
int ret = 0;
int board_id;
if (!g_board_mgr.initialized || !board) {
return -EINVAL;
}
calculet_board_set_state(board, BOARD_STATE_ACTIVE);
mutex_lock(&g_board_mgr.board_mutex);
// 使用IDR分配板卡索引
spin_lock(&g_board_mgr.index_lock);
board_id = idr_alloc(&g_board_mgr.board_idr, board, 0, MAX_CALCULET_BOARDS, GFP_ATOMIC);
spin_unlock(&g_board_mgr.index_lock);
if (board_id < 0) {
ret = board_id;
goto out;
}
board->board_index = board_id;
// 初始化板卡结构
kref_init(&board->ref_count);
// 初始化统计信息
memset(&board->stats, 0, sizeof(board->stats));
board->stats.uptime_start = ktime_get();
atomic64_set(&board->stats.dma_transfers_h2c, 0);
atomic64_set(&board->stats.dma_transfers_c2h, 0);
atomic64_set(&board->stats.dma_bytes_h2c, 0);
atomic64_set(&board->stats.dma_bytes_c2h, 0);
atomic64_set(&board->stats.interrupts_count, 0);
atomic64_set(&board->stats.errors_count, 0);
// 初始化进程监测
INIT_LIST_HEAD(&board->process_list);
mutex_init(&board->process_list_mutex);
atomic_set(&board->process_count, 0);
// 初始化软复位控制
atomic_set(&board->reset_in_progress, 0);
// 初始化资源
board->pcie_resources.resources_valid = true;
// 添加到列表
list_add_tail(&board->board_list, &g_board_mgr.board_list);
atomic_inc(&g_board_mgr.board_count);
atomic_inc(&g_board_mgr.reset_ctrl.boards_total);
// 创建proc条目
snprintf(board->proc_name, sizeof(board->proc_name), "device%d", board->board_index);
ret = calculet_proc_add_device(board);
if (ret < 0) {
list_del(&board->board_list);
atomic_dec(&g_board_mgr.board_count);
atomic_dec(&g_board_mgr.reset_ctrl.boards_total);
spin_lock(&g_board_mgr.index_lock);
idr_remove(&g_board_mgr.board_idr, board->board_index);
spin_unlock(&g_board_mgr.index_lock);
goto out;
}
calculet_log(CALCULET_LOG_DEBUG, "Board %d added successfully", board->board_index);
out:
mutex_unlock(&g_board_mgr.board_mutex);
return ret;
}
void calculet_board_mgr_remove_board(struct calculet_pcie_board *board)
{
if (!board) {
return;
}
calculet_board_set_state(board, BOARD_STATE_REMOVING);
mutex_lock(&g_board_mgr.board_mutex);
// 清理进程监测
calculet_proc_cleanup_processes(board);
// 移除proc条目
calculet_proc_remove_device(board);
// 从列表移除
list_del(&board->board_list);
atomic_dec(&g_board_mgr.board_count);
atomic_dec(&g_board_mgr.reset_ctrl.boards_total);
// 标记资源无效
board->pcie_resources.resources_valid = false;
mutex_unlock(&g_board_mgr.board_mutex);
// 释放引用(这将触发最终清理)
calculet_board_put(board);
calculet_log(CALCULET_LOG_INFO, "Board removed successfully");
}
int calculet_board_mgr_get_all_boards(struct calculet_pcie_board **boards, int max_boards)
{
struct calculet_pcie_board *board;
int count = 0;
if (!g_board_mgr.initialized || !boards) {
return 0;
}
mutex_lock(&g_board_mgr.board_mutex);
list_for_each_entry(board, &g_board_mgr.board_list, board_list) {
if (count >= max_boards) {
break;
}
if (calculet_board_is_operational(board)) {
boards[count] = board;
calculet_board_get(board);
count++;
}
}
mutex_unlock(&g_board_mgr.board_mutex);
return count;
}
// 板卡健康检查
int calculet_board_health_check(struct calculet_pcie_board *board)
{
if (!board || !calculet_board_is_operational(board)) {
return -ENODEV;
}
// 检查PCIe设备状态
if (!board->pDev || pci_channel_offline(board->pDev)) {
calculet_log(CALCULET_LOG_ERROR, "Board %d PCIe device is offline", board->board_index);
return -EIO;
}
// 检查资源有效性
if (!board->pcie_resources.resources_valid) {
calculet_log(CALCULET_LOG_ERROR, "Board %d resources are invalid", board->board_index);
return -EINVAL;
}
// 检查错误率
u64 total_ops = atomic64_read(&board->stats.dma_transfers_h2c) +
atomic64_read(&board->stats.dma_transfers_c2h);
u64 error_rate = total_ops > 0 ?
(atomic64_read(&board->stats.errors_count) * 100) / total_ops : 0;
if (error_rate > 5) { // 错误率超过5%
calculet_log(CALCULET_LOG_WARN, "Board %d high error rate: %llu%%",
board->board_index, error_rate);
return -EFAULT;
}
return 0;
}
// 全局软复位功能
int calculet_global_soft_reset_prepare(struct calculet_pcie_board *board)
{
if (!board) {
return -EINVAL;
}
// 检查是否已经在复位中
if (atomic_cmpxchg(&board->reset_in_progress, 0, 1) != 0) {
calculet_log(CALCULET_LOG_WARN, "Reset already in progress for board %d", board->board_index);
return -EBUSY;
}
mutex_lock(&g_board_mgr.reset_ctrl.reset_mutex);
// 增加正在复位的板卡计数
atomic_inc(&g_board_mgr.reset_ctrl.boards_resetting);
calculet_log(CALCULET_LOG_DEBUG, "Board %d prepared for reset (%d/%d boards resetting)",
board->board_index,
atomic_read(&g_board_mgr.reset_ctrl.boards_resetting),
atomic_read(&g_board_mgr.reset_ctrl.boards_total));
mutex_unlock(&g_board_mgr.reset_ctrl.reset_mutex);
return 0;
}
void calculet_global_soft_reset_complete(struct calculet_pcie_board *board)
{
if (!board) {
return;
}
mutex_lock(&g_board_mgr.reset_ctrl.reset_mutex);
// 减少正在复位的板卡计数
if (atomic_read(&g_board_mgr.reset_ctrl.boards_resetting) > 0) {
atomic_dec(&g_board_mgr.reset_ctrl.boards_resetting);
}
// 清除板卡复位标志
atomic_set(&board->reset_in_progress, 0);
calculet_log(CALCULET_LOG_DEBUG, "Board %d reset completed (%d/%d boards still resetting)",
board->board_index,
atomic_read(&g_board_mgr.reset_ctrl.boards_resetting),
atomic_read(&g_board_mgr.reset_ctrl.boards_total));
// 唤醒等待的线程
wake_up_all(&g_board_mgr.reset_ctrl.reset_wq);
mutex_unlock(&g_board_mgr.reset_ctrl.reset_mutex);
}
bool calculet_global_soft_reset_can_rescan(void)
{
int resetting = atomic_read(&g_board_mgr.reset_ctrl.boards_resetting);
int total = atomic_read(&g_board_mgr.reset_ctrl.boards_total);
// 只有当所有板卡都完成复位或者没有板卡时才能进行rescan
bool can_rescan = (resetting == 0) || (total == 0);
calculet_log(CALCULET_LOG_DEBUG, "Reset status: %d/%d boards resetting, can_rescan=%s",
resetting, total, can_rescan ? "yes" : "no");
return can_rescan;
}
@@ -0,0 +1,169 @@
// SPDX-License-Identifier: GPL-2.0
/**
* Copyright (c) 2024-2025 CALCULET Technologies Ltd. All rights reserved.
*/
#ifndef _CALCULET_BOARD_MGR_H_
#define _CALCULET_BOARD_MGR_H_
#include "ioctl.h"
#include "dma.h"
#include <linux/pci.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/atomic.h>
#include <linux/proc_fs.h>
#include <linux/spinlock.h>
#include <linux/kref.h>
#include <linux/idr.h>
#define MAX_CALCULET_BOARDS 16
struct calculet_dma_controller;
// 板卡状态枚举
enum calculet_board_state {
BOARD_STATE_UNKNOWN = 0,
BOARD_STATE_ACTIVE,
BOARD_STATE_REMOVING
};
// 资源结构体
struct calculet_resource {
u64 phy_address;
resource_size_t address;
size_t size;
struct mutex dma_global_lock; // 只保留DMA全局锁
};
// PCIe资源集合
struct calculet_pcie_resources {
struct calculet_resource dma_registers; // BAR0
struct calculet_resource rt_registers; // BAR2
struct calculet_resource smi_registers; // BAR4
bool resources_valid; // 资源有效性标志
};
// 简化的板卡统计信息
struct calculet_board_stats {
atomic64_t dma_transfers_h2c;
atomic64_t dma_transfers_c2h;
atomic64_t dma_bytes_h2c;
atomic64_t dma_bytes_c2h;
atomic64_t interrupts_count;
atomic64_t errors_count;
ktime_t uptime_start;
};
// 全局软复位控制
struct calculet_global_reset_ctrl {
atomic_t boards_resetting; // 正在复位的板卡数量
atomic_t boards_total; // 总板卡数量
struct mutex reset_mutex; // 复位互斥锁
wait_queue_head_t reset_wq; // 复位等待队列
};
// PCIe板卡结构体(简化)
struct calculet_pcie_board {
struct list_head board_list;
struct pci_dev *pDev;
u32 board_index;
struct kref ref_count;
enum calculet_board_state state; // 板卡状态
struct calculet_pcie_resources pcie_resources;
struct calculet_dma_controller cdma;
bool interrupts_enabled;
// 简化的统计信息
struct calculet_board_stats stats;
// 进程监测相关字段
struct list_head process_list; // 进程列表头
struct mutex process_list_mutex; // 保护进程列表的互斥锁
atomic_t process_count; // 进程计数
// proc相关
struct proc_dir_entry *proc_entry;
char proc_name[32];
// 软复位相关
atomic_t reset_in_progress; // 复位进行中标志
};
// 全局板卡管理器
struct calculet_board_manager {
struct list_head board_list;
struct mutex board_mutex;
atomic_t board_count;
u32 next_board_index;
spinlock_t index_lock;
struct idr board_idr;
bool initialized;
struct calculet_global_reset_ctrl reset_ctrl; // 全局复位控制
};
// 板卡引用计数管理
void calculet_board_get(struct calculet_pcie_board *board);
void calculet_board_put(struct calculet_pcie_board *board);
void calculet_board_release(struct kref *ref);
// 板卡状态管理(简化)
void calculet_board_set_state(struct calculet_pcie_board *board, enum calculet_board_state state);
enum calculet_board_state calculet_board_get_state(struct calculet_pcie_board *board);
bool calculet_board_is_operational(struct calculet_pcie_board *board);
// 函数声明
int calculet_board_mgr_init(void);
void calculet_board_mgr_cleanup(void);
struct calculet_pcie_board* calculet_board_mgr_get_board(u32 index);
void calculet_board_mgr_put_board(struct calculet_pcie_board *board);
int calculet_board_mgr_add_board(struct calculet_pcie_board *board);
void calculet_board_mgr_remove_board(struct calculet_pcie_board *board);
// 统计信息更新(原子版本,无锁)
static inline void calculet_board_stats_update_dma_h2c(struct calculet_pcie_board *board, size_t bytes)
{
if (likely(board && calculet_board_is_operational(board))) {
atomic64_inc(&board->stats.dma_transfers_h2c);
atomic64_add(bytes, &board->stats.dma_bytes_h2c);
}
}
static inline void calculet_board_stats_update_dma_c2h(struct calculet_pcie_board *board, size_t bytes)
{
if (likely(board && calculet_board_is_operational(board))) {
atomic64_inc(&board->stats.dma_transfers_c2h);
atomic64_add(bytes, &board->stats.dma_bytes_c2h);
}
}
static inline void calculet_board_stats_update_interrupt(struct calculet_pcie_board *board)
{
if (likely(board)) {
atomic64_inc(&board->stats.interrupts_count);
}
}
static inline void calculet_board_stats_update_error(struct calculet_pcie_board *board)
{
if (likely(board)) {
atomic64_inc(&board->stats.errors_count);
}
}
// 获取所有板卡列表(用于proc显示)
int calculet_board_mgr_get_all_boards(struct calculet_pcie_board **boards, int max_boards);
// 板卡健康检查和错误处理
int calculet_board_health_check(struct calculet_pcie_board *board);
// 全局软复位功能
int calculet_global_soft_reset_prepare(struct calculet_pcie_board *board);
void calculet_global_soft_reset_complete(struct calculet_pcie_board *board);
bool calculet_global_soft_reset_can_rescan(void);
#endif /* _CALCULET_BOARD_MGR_H_ */
@@ -0,0 +1,195 @@
#include <linux/module.h>
#define INCLUDE_VERMAGIC
#include <linux/build-salt.h>
#include <linux/elfnote-lto.h>
#include <linux/export-internal.h>
#include <linux/vermagic.h>
#include <linux/compiler.h>
#ifdef CONFIG_UNWINDER_ORC
#include <asm/orc_header.h>
ORC_HEADER;
#endif
BUILD_SALT;
BUILD_LTO_INFO;
MODULE_INFO(vermagic, VERMAGIC_STRING);
MODULE_INFO(name, KBUILD_MODNAME);
__visible struct module __this_module
__section(".gnu.linkonce.this_module") = {
.name = KBUILD_MODNAME,
.init = init_module,
#ifdef CONFIG_MODULE_UNLOAD
.exit = cleanup_module,
#endif
.arch = MODULE_ARCH_INIT,
};
#ifdef CONFIG_MITIGATION_RETPOLINE
MODULE_INFO(retpoline, "Y");
#endif
KSYMTAB_DATA(calculet_log_level, "", "");
KSYMTAB_FUNC(calculet_global_stats_init, "", "");
KSYMTAB_FUNC(calculet_global_stats_update, "", "");
KSYMTAB_FUNC(calculet_global_stats_get, "", "");
KSYMTAB_FUNC(calculet_global_stats_cleanup, "", "");
KSYMTAB_FUNC(calculet_random_delay_us, "", "");
KSYMTAB_FUNC(calculet_strdup, "", "");
KSYMTAB_FUNC(calculet_snprintf_safe, "", "");
KSYMTAB_FUNC(calculet_ffs, "", "");
KSYMTAB_FUNC(calculet_fls, "", "");
KSYMTAB_FUNC(calculet_popcount, "", "");
KSYMTAB_FUNC(calculet_get_cycles, "", "");
KSYMTAB_FUNC(calculet_get_monotonic_time, "", "");
KSYMTAB_FUNC(calculet_get_real_time, "", "");
KSYMTAB_FUNC(calculet_crc32, "", "");
KSYMTAB_FUNC(calculet_utils_init, "", "");
KSYMTAB_FUNC(calculet_utils_cleanup, "", "");
SYMBOL_CRC(calculet_log_level, 0xa6c4c065, "");
SYMBOL_CRC(calculet_global_stats_init, 0x3c49c21b, "");
SYMBOL_CRC(calculet_global_stats_update, 0x2703dbf9, "");
SYMBOL_CRC(calculet_global_stats_get, 0x8eaef0c2, "");
SYMBOL_CRC(calculet_global_stats_cleanup, 0xab13d9fc, "");
SYMBOL_CRC(calculet_random_delay_us, 0x41de3c4b, "");
SYMBOL_CRC(calculet_strdup, 0x5ee22703, "");
SYMBOL_CRC(calculet_snprintf_safe, 0x12fec1fb, "");
SYMBOL_CRC(calculet_ffs, 0xb1fb6ba9, "");
SYMBOL_CRC(calculet_fls, 0x70cd63a2, "");
SYMBOL_CRC(calculet_popcount, 0x2171fdb4, "");
SYMBOL_CRC(calculet_get_cycles, 0x2ddb2922, "");
SYMBOL_CRC(calculet_get_monotonic_time, 0xfd0d90b9, "");
SYMBOL_CRC(calculet_get_real_time, 0xfc4423cc, "");
SYMBOL_CRC(calculet_crc32, 0x62005d8a, "");
SYMBOL_CRC(calculet_utils_init, 0xbecbf10a, "");
SYMBOL_CRC(calculet_utils_cleanup, 0xa17436e2, "");
static const struct modversion_info ____versions[]
__used __section("__versions") = {
{ 0xc1514a3b, "free_irq" },
{ 0x22dde395, "pcie_capability_read_word" },
{ 0xa78af5f3, "ioread32" },
{ 0x88db9f48, "__check_object_size" },
{ 0x8134414f, "dma_ops" },
{ 0x9e7d6bd0, "__udelay" },
{ 0x20978fb9, "idr_find" },
{ 0x5a6efd55, "param_ops_uint" },
{ 0x13c49cc2, "_copy_from_user" },
{ 0x381657e7, "devm_kmalloc" },
{ 0x67620c6, "pci_enable_device" },
{ 0x8d522714, "__rcu_read_lock" },
{ 0x4a453f53, "iowrite32" },
{ 0xe1688e24, "proc_create" },
{ 0x94a18028, "pci_iomap" },
{ 0x920e8334, "pci_alloc_irq_vectors" },
{ 0x656e4a6e, "snprintf" },
{ 0xa6257a2f, "complete" },
{ 0x608741b5, "__init_swait_queue_head" },
{ 0x92540fbf, "finish_wait" },
{ 0xeea0e0d, "class_destroy" },
{ 0x355f4c49, "__pci_register_driver" },
{ 0x7d628444, "memcpy_fromio" },
{ 0xb52801ce, "pci_disable_msi" },
{ 0xd4defbf9, "pci_request_regions" },
{ 0x69acdf38, "memcpy" },
{ 0x88e5f076, "remap_pfn_range" },
{ 0x37a0cba, "kfree" },
{ 0x7ab1f0cb, "pcpu_hot" },
{ 0x7369f212, "seq_lseek" },
{ 0xe964de4b, "proc_create_data" },
{ 0xc3055d20, "usleep_range_state" },
{ 0x8c26d495, "prepare_to_wait_event" },
{ 0xb3f7646e, "kthread_should_stop" },
{ 0xe2964344, "__wake_up" },
{ 0x10ed95a8, "pci_irq_vector" },
{ 0x148653, "vsnprintf" },
{ 0xddbeeecc, "pci_lock_rescan_remove" },
{ 0xba8fbd64, "_raw_spin_lock" },
{ 0xe0872c22, "pci_unregister_driver" },
{ 0xbdfb6dbb, "__fentry__" },
{ 0xcfabba12, "wake_up_process" },
{ 0x4b517ad, "pci_read_config_dword" },
{ 0x122c3a7e, "_printk" },
{ 0x8ddd8aad, "schedule_timeout" },
{ 0x1000e51, "schedule" },
{ 0xf0fdf6cb, "__stack_chk_fail" },
{ 0x296695f, "refcount_warn_saturate" },
{ 0x87a21cb3, "__ubsan_handle_out_of_bounds" },
{ 0x4323050a, "pci_find_capability" },
{ 0x7665a95b, "idr_remove" },
{ 0x9f46ced8, "__sw_hweight64" },
{ 0xfe487975, "init_wait_entry" },
{ 0x98378a1d, "cc_mkdec" },
{ 0xb8f11603, "idr_alloc" },
{ 0x92d5838e, "request_threaded_irq" },
{ 0x2469810f, "__rcu_read_unlock" },
{ 0xa4bf0f83, "class_create" },
{ 0x4c03a563, "random_kmalloc_seed" },
{ 0x69dd3b5b, "crc32_le" },
{ 0x4dfa8d4b, "mutex_lock" },
{ 0x7475b515, "dma_alloc_attrs" },
{ 0x2e5f3dce, "pci_read_config_word" },
{ 0x9166fada, "strncpy" },
{ 0x689f3974, "kthread_stop" },
{ 0xcefb0c9f, "__mutex_init" },
{ 0x89940875, "mutex_lock_interruptible" },
{ 0x71cf81bb, "pci_iounmap" },
{ 0x8e17b3ae, "idr_destroy" },
{ 0xa4eda9c3, "proc_mkdir" },
{ 0x2c015e41, "from_kuid" },
{ 0x6794def6, "pci_set_master" },
{ 0x25974000, "wait_for_completion" },
{ 0x5b8239ca, "__x86_return_thunk" },
{ 0x6b10bee1, "_copy_to_user" },
{ 0xd9a5ea54, "__init_waitqueue_head" },
{ 0xac3c422b, "proc_remove" },
{ 0x42b507fc, "dma_set_coherent_mask" },
{ 0x3817aecb, "kthread_create_on_node" },
{ 0xc1d9b323, "seq_read" },
{ 0x3c3ff9fd, "sprintf" },
{ 0xc688b77e, "device_create_with_groups" },
{ 0xa9962270, "dma_free_attrs" },
{ 0x3213f038, "mutex_unlock" },
{ 0x5790e7a0, "pci_unlock_rescan_remove" },
{ 0x2e1c1b75, "param_ops_bool" },
{ 0x8bf35ae8, "pci_release_regions" },
{ 0x6423a399, "init_user_ns" },
{ 0xb329595b, "__register_chrdev" },
{ 0xe9b868f, "device_destroy" },
{ 0xb43f9365, "ktime_get" },
{ 0xc00e2b80, "seq_printf" },
{ 0xd36dc10c, "get_random_u32" },
{ 0x2e4b8e3f, "pci_disable_device" },
{ 0xe16cab4a, "boot_cpu_data" },
{ 0x8e66928c, "single_release" },
{ 0x935c45ad, "dma_set_mask" },
{ 0x904c0891, "pci_stop_and_remove_bus_device" },
{ 0xbf55f104, "kmalloc_trace" },
{ 0x46cf10eb, "cachemode2protval" },
{ 0x54b1fac6, "__ubsan_handle_load_invalid_value" },
{ 0x754d539c, "strlen" },
{ 0xa7e3af6c, "param_ops_int" },
{ 0xbdbea117, "_dev_printk" },
{ 0x2e0257b3, "pci_rescan_bus" },
{ 0x46b0be46, "single_open" },
{ 0xb5b54b34, "_raw_spin_unlock" },
{ 0x4503bd8e, "pci_free_irq_vectors" },
{ 0xf9a482f9, "msleep" },
{ 0xc4f0da12, "ktime_get_with_offset" },
{ 0xeb233a45, "__kmalloc" },
{ 0xe2c17b5d, "__SCT__might_resched" },
{ 0x1004e946, "kmalloc_caches" },
{ 0x6bc3fbc0, "__unregister_chrdev" },
{ 0x73776b79, "module_layout" },
};
MODULE_INFO(depends, "");
MODULE_ALIAS("pci:v00005678d00001234sv*sd*bc*sc*i*");
MODULE_ALIAS("pci:v000020F5d*sv*sd*bc*sc*i*");
MODULE_ALIAS("pci:v000016C3d0000ABCDsv*sd*bc*sc*i*");
MODULE_INFO(srcversion, "E0486E16735A427A5167C20");
@@ -0,0 +1,71 @@
// SPDX-License-Identifier: GPL-2.0
/**
* Copyright (c) 2024-2025 CALCULET Technologies Ltd. All rights reserved.
*/
#ifndef _CALCULET_PCI_H_
#define _CALCULET_PCI_H_
#include "board_mgr.h"
#include "dma.h"
#include "ioctl.h"
#include "utils.h"
#include <linux/pci.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#define DRIVER_NAME "calculet"
#define DEVICE_NODE_NAME "calculet"
#define PCI_VENDOR_ID_CALCULET 0x1234
#define PCI_DEVICE_ID_CALCULET 0x5678
#define CALCULET_PCIE_DMA_REGS_BAR (0)
#define CALCULET_PCIE_RT_REGS_BAR (2)
#define CALCULET_PCIE_SMI_REGS_BAR (4)
// DMA中断写入地址寄存器定义
#define DMA_WRITE_DONE_IMWR_ADDRESS_L (0x60)
#define DMA_WRITE_DONE_IMWR_ADDRESS_H (0x64)
#define DMA_WRITE_ABORT_IMWR_ADDRESS_L (0x68)
#define DMA_WRITE_ABORT_IMWR_ADDRESS_H (0x6C)
#define DMA_WRITE_IMWR_DATA (0x70)
#define DMA_READ_DONE_IMWR_ADDRESS_L (0xCC)
#define DMA_READ_DONE_IMWR_ADDRESS_H (0xD0)
#define DMA_READ_ABORT_IMWR_ADDRESS_L (0xD4)
#define DMA_READ_ABORT_IMWR_ADDRESS_H (0xD8)
#define DMA_READ_IMWR_DATA (0xdc)
//
#define CALCULET_OS_STATUS_REG 0x223810C
#define CALCULET_RT_STATUS_REG 0x2238250
// 函数声明
int calculet_enable_interrupts(struct calculet_pcie_board *board);
void calculet_disable_interrupts(struct calculet_pcie_board *board);
int calculet_activate_board(struct calculet_pcie_board *board);
// 资源管理函数
u64 calculet_resource_read64(struct calculet_resource *resource, size_t offset);
u32 calculet_resource_read32(struct calculet_resource *resource, size_t offset);
void calculet_resource_write64(struct calculet_resource *resource, size_t offset, u64 value);
void calculet_resource_write32(struct calculet_resource *resource, size_t offset, u32 value);
void calculet_read_strings(char* buf, struct calculet_resource *resource, size_t offset, u64 count);
int calculet_bar64_transfer(struct calculet_resource *resource, struct calculet_bar64_transfer_params *transfer);
int calculet_bar32_transfer(struct calculet_resource *resource, struct calculet_bar32_transfer_params *transfer);
// PCIe功能函数
int calculet_pcie_bar64_transfer(struct calculet_pcie_resources *resources, struct calculet_bar64_transfer_params *transfer);
int calculet_pcie_bar32_transfer(struct calculet_pcie_resources *resources, struct calculet_bar32_transfer_params *transfer);
void calculet_pcie_enable_interrupts(struct pci_dev *pDev, struct calculet_pcie_resources *resources);
void calculet_pcie_enable_poll(struct pci_dev *pDev, struct calculet_pcie_resources *resources, struct calculet_dma_poll *poll);
void calculet_pcie_disable_interrupts(struct pci_dev *pDev, struct calculet_pcie_resources* resources);
// 板卡管理
int pcie_resources_init(struct pci_dev *pdev, struct calculet_pcie_resources *resources);
void pcie_resources_release(struct pci_dev *pdev, struct calculet_pcie_resources *resources);
#endif /* _CALCULET_PCI_H_ */
@@ -0,0 +1,78 @@
#!/bin/bash
# 1. 获取当前 dkms.conf 的绝对路径
DKMS_CONF_PATH="${BASH_SOURCE[0]}"
# 2. 获取真实的物理目录路径
DKMS_SRC_DIR="$(cd "$(dirname "$(readlink -f "${DKMS_CONF_PATH}")")" && pwd)"
# 3. 提取目录名
CURRENT_DIR=$(basename "${DKMS_SRC_DIR}")
# 4. 解析PACKAGE_NAME和PACKAGE_VERSION
PACKAGE_NAME=$(echo "${CURRENT_DIR}" | sed 's/-[0-9][0-9.]*$//')
PACKAGE_VERSION=$(echo "${CURRENT_DIR}" | sed 's/^.*-\([0-9][0-9.]*\)$/\1/')
if [ "$PACKAGE_NAME" = "$CURRENT_DIR" ] || [ -z "$PACKAGE_VERSION" ]; then
echo "错误: 目录名 '$CURRENT_DIR' 不符合 '<包名>-<版本号>' 格式" >&2
exit 1
fi
echo "提取结果: PACKAGE_NAME=$PACKAGE_NAME, PACKAGE_VERSION=$PACKAGE_VERSION" >&2
# 5. Makefile 路径校验
MAKEFILE_ABS_PATH="${DKMS_SRC_DIR}/Makefile"
if [ ! -f "${MAKEFILE_ABS_PATH}" ]; then
echo "错误: 在目录 ${DKMS_SRC_DIR} 下找不到 Makefile" >&2
exit 1
fi
# 6. 从 Makefile 中提取或推断驱动名称
DRIVER_NAME=$(grep -E '^[[:space:]]*DRIVER_NAME[[:space:]]*[:?]?=' "${MAKEFILE_ABS_PATH}" | head -1 | sed -E 's/^[^=]*=[[:space:]]*//' | sed 's/[[:space:]]*$//' | tr -d '"' | tr -d "'")
# 去掉 .ko 或 .o 后缀
DRIVER_NAME=$(echo "$DRIVER_NAME" | sed 's/\.ko$//' | sed 's/\.o$//')
if [ -z "$DRIVER_NAME" ] || [ "$DRIVER_NAME" = "Makefile" ]; then
echo "警告: Makefile 中未找到 DRIVER_NAME 定义,尝试从 obj-m 提取..." >&2
# 降级抓取 obj-m
DRIVER_NAME=$(grep -E '^obj-m[[:space:]]*[\+:]?=' "${MAKEFILE_ABS_PATH}" | head -1 | sed -E 's/^obj-m[[:space:]]*[\+:]?=[[:space:]]*//' | sed 's/[[:space:]]*$//' | tr -d '"' | tr -d "'")
# 去掉所有 .o 和 .ko
DRIVER_NAME=$(echo "$DRIVER_NAME" | sed 's/\.ko$//' | sed 's/\.o$//')
if [ -n "$DRIVER_NAME" ]; then
echo "从 obj-m 推断驱动名称: $DRIVER_NAME" >&2
else
echo "错误: 无法从 Makefile 中确定驱动名称" >&2
exit 1
fi
else
echo "从 Makefile 提取驱动名称: $DRIVER_NAME" >&2
fi
# 7. DKMS 标准配置参数定义
PACKAGE_NAME="${PACKAGE_NAME}"
PACKAGE_VERSION="${PACKAGE_VERSION}"
BUILT_MODULE_NAME[0]="${DRIVER_NAME}"
BUILT_MODULE_LOCATION[0]="."
DEST_MODULE_LOCATION[0]="/kernel/drivers/misc"
# 编译与清理命令定义
MAKE[0]="make -C ${kernel_source_dir} M=${dkms_tree}/${PACKAGE_NAME}/${PACKAGE_VERSION}/build"
CLEAN="make -C ${kernel_source_dir} M=${dkms_tree}/${PACKAGE_NAME}/${PACKAGE_VERSION}/build clean"
AUTOINSTALL="yes"
# # 输出最终配置信息
# echo "========================================" >&2
# echo "DKMS 最终配置:" >&2
# echo " PACKAGE_NAME: $PACKAGE_NAME" >&2
# echo " PACKAGE_VERSION: $PACKAGE_VERSION" >&2
# echo " BUILT_MODULE_NAME: ${BUILT_MODULE_NAME[0]}" >&2
# echo " BUILT_MODULE_LOCATION: ${BUILT_MODULE_LOCATION[0]}" >&2
# echo " DEST_MODULE_LOCATION: ${DEST_MODULE_LOCATION[0]}" >&2
# echo "========================================" >&2
@@ -0,0 +1,966 @@
// SPDX-License-Identifier: GPL-2.0
/**
* Copyright (c) 2024-2025 CALCULET Technologies Ltd. All rights reserved.
*/
#include "dma.h"
#include "board_mgr.h"
#include "utils.h"
#include <linux/sched.h>
#include <linux/version.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <linux/mm.h>
#include <linux/uaccess.h>
#include <asm/cacheflush.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
#include <linux/dma-map-ops.h>
#else
#include <linux/dma-mapping.h>
#endif
static int calculet_set_dma_mask(struct device *dev)
{
int err = -EINVAL;
/* Check and configure DMA length */
if (!(err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)))) {
calculet_dev_log(CALCULET_LOG_INFO, dev, "Enabled 64 bit dma");
} else if (!(err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48)))) {
calculet_dev_log(CALCULET_LOG_DEBUG, dev, "Enabled 48 bit dma");
} else if (!(err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(40)))) {
calculet_dev_log(CALCULET_LOG_DEBUG, dev, "Enabled 40 bit dma");
} else if (!(err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(36)))) {
calculet_dev_log(CALCULET_LOG_DEBUG, dev, "Enabled 36 bit dma");
} else if (!(err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)))) {
calculet_dev_log(CALCULET_LOG_INFO, dev, "Enabled 32 bit dma");
} else {
calculet_dev_log(CALCULET_LOG_ERROR, dev, "Error enabling dma %d", err);
return err;
}
return 0;
}
// DMA通道状态管理函数(简化,减少不必要的锁)
void calculet_dma_channel_set_state(struct calculet_dma_channel *channel,
enum calculet_dma_channel_state state)
{
if (channel && channel->state != state) {
calculet_log(CALCULET_LOG_DEBUG, "DMA channel %d state change: %d -> %d",
channel->channel_index, channel->state, state);
WRITE_ONCE(channel->state, state);
channel->last_used = ktime_get();
}
}
enum calculet_dma_channel_state calculet_dma_channel_get_state(struct calculet_dma_channel *channel)
{
return channel ? READ_ONCE(channel->state) : DMA_CHANNEL_ERROR;
}
bool calculet_dma_channel_is_available(struct calculet_dma_channel *channel)
{
if (!channel) return false;
enum calculet_dma_channel_state state = calculet_dma_channel_get_state(channel);
return (state == DMA_CHANNEL_IDLE);
}
// 通道分配和释放(简化,使用原子操作)
int calculet_dma_allocate_channel(struct calculet_dma_controller *controller,
enum calculet_transfer_direction direction,
int *channel_id)
{
unsigned long *bitmap;
struct calculet_dma_channel *channels;
int max_channels;
int id;
if (!controller || !channel_id) {
return -EINVAL;
}
if (direction == TRANSFER_H2C_DIRECTION) {
bitmap = &controller->read_channels_bitmap;
channels = controller->dma_rdch_s;
max_channels = CALCULET_READ_CHANNELS;
} else {
bitmap = &controller->write_channels_bitmap;
channels = controller->dma_wrch_s;
max_channels = CALCULET_WRITE_CHANNELS;
}
// 查找可用通道(无锁版本)
do {
id = find_first_zero_bit(bitmap, max_channels);
if (id >= max_channels) {
return -EBUSY;
}
// 检查通道状态
if (!calculet_dma_channel_is_available(&channels[id])) {
continue;
}
} while (test_and_set_bit(id, bitmap));
calculet_dma_channel_set_state(&channels[id], DMA_CHANNEL_BUSY);
*channel_id = id;
calculet_log(CALCULET_LOG_DEBUG, "Allocated %s channel %d",
direction == TRANSFER_H2C_DIRECTION ? "H2C" : "C2H", id);
return 0;
}
void calculet_dma_release_channel(struct calculet_dma_controller *controller,
enum calculet_transfer_direction direction,
int channel_id)
{
unsigned long *bitmap;
struct calculet_dma_channel *channels;
int max_channels;
if (!controller) {
return;
}
if (direction == TRANSFER_H2C_DIRECTION) {
bitmap = &controller->read_channels_bitmap;
channels = controller->dma_rdch_s;
max_channels = CALCULET_READ_CHANNELS;
} else {
bitmap = &controller->write_channels_bitmap;
channels = controller->dma_wrch_s;
max_channels = CALCULET_WRITE_CHANNELS;
}
if (channel_id >= 0 && channel_id < max_channels) {
clear_bit(channel_id, bitmap);
calculet_dma_channel_set_state(&channels[channel_id], DMA_CHANNEL_IDLE);
calculet_log(CALCULET_LOG_DEBUG, "Released %s channel %d",
direction == TRANSFER_H2C_DIRECTION ? "H2C" : "C2H", channel_id);
}
}
// 传输上下文管理(简化)
struct calculet_dma_transfer_context* calculet_dma_create_transfer_context(pid_t pid)
{
struct calculet_dma_transfer_context *ctx;
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx) {
return NULL;
}
init_completion(&ctx->completion);
ctx->result = 0;
ctx->start_time = ktime_get();
ctx->requester_pid = pid;
atomic_set(&ctx->ref_count, 1);
return ctx;
}
void calculet_dma_destroy_transfer_context(struct calculet_dma_transfer_context *ctx)
{
if (ctx) {
kfree(ctx);
}
}
void calculet_dma_transfer_context_get(struct calculet_dma_transfer_context *ctx)
{
if (ctx) {
atomic_inc(&ctx->ref_count);
}
}
void calculet_dma_transfer_context_put(struct calculet_dma_transfer_context *ctx)
{
if (ctx && atomic_dec_and_test(&ctx->ref_count)) {
calculet_dma_destroy_transfer_context(ctx);
}
}
int calculet_dma_controller_init(struct calculet_dma_controller *controller, struct device *dev, bool poll_en)
{
int i;
int err = 0;
struct calculet_dma_poll *poll;
if (!dev) {
calculet_log(CALCULET_LOG_ERROR, "Device pointer is NULL");
return -EINVAL;
}
controller->dev = dev;
controller->controller_enabled = false;
// 初始化控制器级别的锁和状态
mutex_init(&controller->controller_mutex);
mutex_init(&controller->allocation_mutex);
atomic_set(&controller->active_transfers, 0);
// 初始化通道分配位图
controller->read_channels_bitmap = 0;
controller->write_channels_bitmap = 0;
/* Check and configure DMA length */
err = calculet_set_dma_mask(dev);
if (0 > err) {
return err;
}
if (get_dma_ops(controller->dev)) {
calculet_dev_log(CALCULET_LOG_DEBUG, controller->dev, "Using specialized dma_ops=%ps", get_dma_ops(controller->dev));
}
else {
calculet_dev_log(CALCULET_LOG_WARN, dev, "No specialized dma_ops found");
}
// 初始化读通道(简化初始化)
for(i = 0; i < CALCULET_READ_CHANNELS; i++) {
struct calculet_dma_channel *channel = &controller->dma_rdch_s[i];
memset(channel, 0, sizeof(*channel));
channel->channel_index = i;
channel->state = DMA_CHANNEL_IDLE;
spin_lock_init(&channel->state_lock);
mutex_init(&channel->buffer_mutex);
mutex_init(&channel->transfer_mutex);
atomic_set(&channel->buffer_users, 0);
atomic_set(&channel->transfer_finish, 0);
atomic64_set(&channel->transfer_count, 0);
atomic64_set(&channel->transfer_bytes, 0);
init_waitqueue_head(&channel->channel_wq);
}
// 初始化写通道
for(i = 0; i < CALCULET_WRITE_CHANNELS; i++) {
struct calculet_dma_channel *channel = &controller->dma_wrch_s[i];
memset(channel, 0, sizeof(*channel));
channel->channel_index = i;
channel->state = DMA_CHANNEL_IDLE;
spin_lock_init(&channel->state_lock);
mutex_init(&channel->buffer_mutex);
mutex_init(&channel->transfer_mutex);
atomic_set(&channel->buffer_users, 0);
atomic_set(&channel->transfer_finish, 0);
atomic64_set(&channel->transfer_count, 0);
atomic64_set(&channel->transfer_bytes, 0);
init_waitqueue_head(&channel->channel_wq);
}
// 初始化轮询模式
if(poll_en) {
/* 分配 DMA Kthread 结构体内存 */
if(!controller->dma_kthread) {
controller->dma_kthread = devm_kzalloc(dev, sizeof(struct calculet_dma_kthread), GFP_KERNEL);
if (!controller->dma_kthread) {
return -ENOMEM;
}
}
/* 分配 DMA Poll 结构体内存 */
if (!controller->dma_kthread->dma_poll) {
controller->dma_kthread->dma_poll = devm_kzalloc(dev, sizeof(struct calculet_dma_poll), GFP_KERNEL);
if (!controller->dma_kthread->dma_poll) {
return -ENOMEM;
}
}
poll = controller->dma_kthread->dma_poll;
poll->status_region = dma_alloc_coherent(dev, sizeof(uint32_t), &poll->status_region_phys, GFP_KERNEL);
if (!poll->status_region) {
calculet_dev_log(CALCULET_LOG_ERROR, dev, "Failed to allocate coherent memory for status region");
return -ENOMEM;
}
spin_lock_init(&poll->poll_lock);
atomic_set(&poll->poll_active, 0);
for (i = 0; i < CALCULET_TOTAL_CHANNELS; i++) {
poll->channel_data[i] = i + 100;
}
// 初始化内核线程
atomic_set(&controller->dma_kthread->should_stop, 0);
init_completion(&controller->dma_kthread->thread_completion);
}
controller->controller_enabled = true;
calculet_log(CALCULET_LOG_DEBUG, "DMA controller initialized (poll_mode: %s)",
poll_en ? "enabled" : "disabled");
return 0;
}
void calculet_dma_controller_deinit(struct calculet_dma_controller *controller, bool poll_en)
{
int i;
struct calculet_dma_poll *poll;
if (!controller) {
return;
}
controller->controller_enabled = false;
// 等待所有活动传输完成
while (atomic_read(&controller->active_transfers) > 0) {
msleep(10);
}
// 清理读通道
for(i = 0; i < CALCULET_READ_CHANNELS; i++) {
struct calculet_dma_channel *channel = &controller->dma_rdch_s[i];
// 设置通道为禁用状态
calculet_dma_channel_set_state(channel, DMA_CHANNEL_DISABLED);
// 释放缓冲区
mutex_lock(&channel->buffer_mutex);
if (channel->kernel_buf) {
dma_free_coherent(controller->dev, channel->dma_len,
channel->kernel_buf, channel->dma_handle);
channel->kernel_buf = NULL;
channel->dma_len = 0;
}
mutex_unlock(&channel->buffer_mutex);
// 清理传输上下文
if (channel->current_transfer) {
calculet_dma_transfer_context_put(channel->current_transfer);
channel->current_transfer = NULL;
}
}
// 清理写通道
for(i = 0; i < CALCULET_WRITE_CHANNELS; i++) {
struct calculet_dma_channel *channel = &controller->dma_wrch_s[i];
// 设置通道为禁用状态
calculet_dma_channel_set_state(channel, DMA_CHANNEL_DISABLED);
// 释放缓冲区
mutex_lock(&channel->buffer_mutex);
if (channel->kernel_buf) {
dma_free_coherent(controller->dev, channel->dma_len,
channel->kernel_buf, channel->dma_handle);
channel->kernel_buf = NULL;
channel->dma_len = 0;
}
mutex_unlock(&channel->buffer_mutex);
// 清理传输上下文
if (channel->current_transfer) {
calculet_dma_transfer_context_put(channel->current_transfer);
channel->current_transfer = NULL;
}
}
// 清理轮询相关资源
if(poll_en && controller->dma_kthread) {
poll = controller->dma_kthread->dma_poll;
if (poll) {
for (i = 0; i < CALCULET_TOTAL_CHANNELS; i++) {
poll->channel_data[i] = 0;
}
if (poll->status_region) {
dma_free_coherent(controller->dev, sizeof(uint32_t),
poll->status_region, poll->status_region_phys);
poll->status_region = NULL;
}
}
}
calculet_log(CALCULET_LOG_DEBUG, "DMA controller deinitialized");
}
// DMA参数验证
int calculet_dma_validate_transfer_params(struct calculet_dma_transfer_channels_params *params)
{
if (!params) {
return -EINVAL;
}
// 检查传输方向
if (params->dma_transfer_direction != TRANSFER_H2C_DIRECTION &&
params->dma_transfer_direction != TRANSFER_C2H_DIRECTION) {
return -EINVAL;
}
// 检查通道ID
if (params->dma_transfer_direction == TRANSFER_H2C_DIRECTION) {
if (params->dma_channel_id < 0 || params->dma_channel_id >= CALCULET_READ_CHANNELS) {
return -EINVAL;
}
} else {
if (params->dma_channel_id < 0 || params->dma_channel_id >= CALCULET_WRITE_CHANNELS) {
return -EINVAL;
}
}
// 检查传输大小
if (params->dma_channel_size == 0 || params->dma_channel_size > (32 * 1024 * 1024)) {
return -EINVAL;
}
// 检查地址对齐
if (params->device_address & 0x3) {
calculet_log(CALCULET_LOG_WARN, "Device address 0x%llx is not 4-byte aligned",
params->device_address);
}
return 0;
}
// 安全的DMA传输函数
long calculet_dma_safe_transfer(struct calculet_dma_controller *controller,
struct calculet_dma_transfer_channels_params *params,
struct calculet_resource *resource)
{
struct calculet_dma_channel *channel;
struct calculet_dma_transfer_context *ctx;
long ret;
int ch_id;
if (!controller || !params || !resource) {
return -EINVAL;
}
// 验证传输参数
ret = calculet_dma_validate_transfer_params(params);
if (ret) {
return ret;
}
// 检查控制器状态
if (!controller->controller_enabled) {
return -ENODEV;
}
ch_id = params->dma_channel_id;
// 获取通道
if (params->dma_transfer_direction == TRANSFER_H2C_DIRECTION) {
if (ch_id >= CALCULET_READ_CHANNELS) {
return -EINVAL;
}
channel = &controller->dma_rdch_s[ch_id];
} else {
if (ch_id >= CALCULET_WRITE_CHANNELS) {
return -EINVAL;
}
channel = &controller->dma_wrch_s[ch_id];
}
// 检查通道状态
if (!calculet_dma_channel_is_available(channel)) {
return -EBUSY;
}
// 创建传输上下文
ctx = calculet_dma_create_transfer_context(current->pid);
if (!ctx) {
return -ENOMEM;
}
// 设置通道参数
channel->dma_channel_size = params->dma_channel_size;
channel->buffer = params->host_address;
channel->dst_address = params->device_address;
channel->current_transfer = ctx;
// 增加活动传输计数
atomic_inc(&controller->active_transfers);
// 执行传输
if (params->dma_transfer_direction == TRANSFER_H2C_DIRECTION) {
ret = calculet_dma_h2c_transfer(controller->dev, channel, resource);
} else {
ret = calculet_dma_c2h_transfer(controller->dev, channel, resource);
}
// 清理
channel->current_transfer = NULL;
calculet_dma_transfer_context_put(ctx);
atomic_dec(&controller->active_transfers);
return ret;
}
static long calculet_block_dma_transfer_ioctl(struct calculet_resource *resource, struct calculet_dma_controller *controller, unsigned long arg)
{
struct calculet_dma_transfer_channels_params input;
long ret = -1;
if (copy_from_user(&input, (void *)arg, sizeof(input))) {
calculet_dev_log(CALCULET_LOG_ERROR, controller->dev, "copy_from_user fail");
return -EFAULT;
}
// 使用安全的传输函数
ret = calculet_dma_safe_transfer(controller, &input, resource);
return ret;
}
int calculet_dma_poll_ioctl(struct calculet_dma_controller *controller)
{
struct calculet_dma_poll *poll;
int region_data = 0;
int i;
if (!controller || !controller->dma_kthread || !controller->dma_kthread->dma_poll) {
return -EINVAL;
}
poll = controller->dma_kthread->dma_poll;
spin_lock(&poll->poll_lock);
region_data = *poll->status_region;
if(region_data != 0) {
for(i = 0; i < CALCULET_TOTAL_CHANNELS; i++)
{
if(poll->channel_data[i] == region_data) {
if(i < CALCULET_TOTAL_CHANNELS/2) {
atomic_set(&controller->dma_rdch_s[i].transfer_finish, 1);
wake_up_interruptible(&controller->dma_rdch_s[i].channel_wq);
break;
}
else {
atomic_set(&controller->dma_wrch_s[i - CALCULET_READ_CHANNELS].transfer_finish, 1);
wake_up_interruptible(&controller->dma_wrch_s[i - CALCULET_READ_CHANNELS].channel_wq);
break;
}
}
}
*poll->status_region = 0;
}
spin_unlock(&poll->poll_lock);
return region_data;
}
long calculet_dma_ioctl(struct calculet_resource *resource, struct calculet_dma_controller *controller, unsigned int cmd, unsigned long arg)
{
if (!resource || !controller) {
return -EINVAL;
}
switch(cmd) {
case CALCULET_BLOCK_DMA_TRANSFER:
return calculet_block_dma_transfer_ioctl(resource, controller, arg);
case CALCULET_INTERRUPT_POLL:
return calculet_dma_poll_ioctl(controller);
default:
calculet_dev_log(CALCULET_LOG_ERROR, controller->dev, "Invalid DMA ioctl code 0x%x (nr: %d)", cmd, _IOC_NR(cmd));
return -ENOTTY;
}
}
//block dma api
long calculet_dma_h2c_transfer(struct device *dev, struct calculet_dma_channel *channel, struct calculet_resource *resource)
{
int ch = channel->channel_index;
unsigned long timeout = msecs_to_jiffies(100000); // 设置超时时间为100秒
struct calculet_pcie_board *board = dev_get_drvdata(dev);
long ret = 0;
int transfer_result;
if (!board || !calculet_board_is_operational(board)) {
return -ENODEV;
}
CALCULET_PERF_START(h2c_transfer);
// 获取传输互斥锁
if (mutex_lock_interruptible(&channel->transfer_mutex)) {
return -ERESTARTSYS;
}
// 缓冲区管理
mutex_lock(&channel->buffer_mutex);
if(channel->dma_channel_size <= DMA_CHANNEL_MAX_SIZE) {
if(channel->kernel_buf == NULL) {
channel->kernel_buf = dma_alloc_coherent(dev, DMA_CHANNEL_MAX_SIZE, &channel->dma_handle, GFP_KERNEL);
if (!channel->kernel_buf) {
calculet_dev_log(CALCULET_LOG_ERROR, dev, "Failed to allocate DMA buffer");
ret = -ENOMEM;
goto error_unlock_buffer;
}
}
} else {
calculet_dev_log(CALCULET_LOG_ERROR, dev, "H2C transfer size %ld is too large", channel->dma_channel_size);
ret = -EINVAL;
goto error_unlock_buffer;
}
// if(channel->dma_len != channel->dma_channel_size) {
// if(channel->kernel_buf == NULL) {
// channel->kernel_buf = dma_alloc_coherent(dev, channel->dma_channel_size, &channel->dma_handle, GFP_KERNEL);
// if (!channel->kernel_buf) {
// calculet_dev_log(CALCULET_LOG_ERROR, dev, "Failed to allocate DMA buffer");
// ret = -ENOMEM;
// goto error_unlock_buffer;
// }
// } else {
// dma_free_coherent(dev, channel->dma_len, channel->kernel_buf, channel->dma_handle);
// channel->kernel_buf = dma_alloc_coherent(dev, channel->dma_channel_size, &channel->dma_handle, GFP_KERNEL);
// if (!channel->kernel_buf) {
// calculet_dev_log(CALCULET_LOG_ERROR, dev, "Failed to allocate DMA buffer");
// ret = -ENOMEM;
// goto error_unlock_buffer;
// }
// }
// channel->dma_len = channel->dma_channel_size;
// }
atomic_inc(&channel->buffer_users);
mutex_unlock(&channel->buffer_mutex);
// 从用户空间拷贝数据到DMA一致性内存
if (copy_from_user(channel->kernel_buf, (void __user *)channel->buffer, channel->dma_channel_size)) {
calculet_dev_log(CALCULET_LOG_ERROR, dev, "Failed to copy_from_user");
ret = -EFAULT;
goto error_cleanup;
}
// 配置DMA寄存器
iowrite32(0x10, (u8*)resource->address + DMA_RDCH_CONTROL1_OFF(ch));
iowrite32(PCI_DMA_L(channel->dma_channel_size), (u8*)resource->address + DMA_RDCH_SIZE_OFF(ch));
iowrite32(PCI_DMA_L(channel->dma_handle), (u8*)resource->address + DMA_RDCH_SAR_LOW_OFF(ch));
iowrite32(PCI_DMA_H(channel->dma_handle), (u8*)resource->address + DMA_RDCH_SAR_HIGH_OFF(ch));
iowrite32(PCI_DMA_L(channel->dst_address), (u8*)resource->address + DMA_RDCH_DAR_LOW_OFF(ch));
iowrite32(PCI_DMA_H(channel->dst_address), (u8*)resource->address + DMA_RDCH_DAR_HIGH_OFF(ch));
// 重置完成标志
atomic_set(&channel->transfer_finish, 0);
// 使用资源结构体中的锁
mutex_lock(&resource->dma_global_lock);
iowrite32(ch, (u8*)resource->address + DMA_READ_DOORBELL_OFF);
mutex_unlock(&resource->dma_global_lock);
// 等待传输完成或超时
transfer_result = wait_event_interruptible_timeout(channel->channel_wq,
atomic_read(&channel->transfer_finish) != 0,
timeout);
if (transfer_result > 0) {
int finish_status = atomic_read(&channel->transfer_finish);
if (finish_status == 1) {
// 传输成功
calculet_board_stats_update_dma_h2c(board, channel->dma_channel_size);
calculet_dma_update_channel_stats(channel, channel->dma_channel_size, true);
ret = 0;
} else {
// 传输错误
calculet_dev_log(CALCULET_LOG_ERROR, dev, "H2C transfer failed with status %d", finish_status);
calculet_board_stats_update_error(board);
calculet_dma_update_channel_stats(channel, 0, false);
ret = finish_status;
}
} else if (transfer_result == 0) {
// 超时
calculet_dev_log(CALCULET_LOG_ERROR, dev, "H2C transfer timed out");
calculet_board_stats_update_error(board);
calculet_dma_update_channel_stats(channel, 0, false);
ret = -ETIMEDOUT;
} else {
// 被信号中断
calculet_dev_log(CALCULET_LOG_ERROR, dev, "H2C transfer interrupted");
ret = -ERESTARTSYS;
}
CALCULET_PERF_END(h2c_transfer, "channel %d, size %ld, result %ld",
ch, channel->dma_channel_size, ret);
calculet_dev_log(CALCULET_LOG_DEBUG, dev, "H2C transfer: channel %d, src 0x%llx, dst 0x%llx, size %ld, result %ld",
ch, channel->dma_handle, channel->dst_address, channel->dma_channel_size, ret);
error_cleanup:
mutex_lock(&channel->buffer_mutex);
if (atomic_dec_and_test(&channel->buffer_users)) {
// 如果发生错误,释放缓冲区
if (ret != 0 && channel->kernel_buf) {
dma_free_coherent(dev, channel->dma_channel_size, channel->kernel_buf, channel->dma_handle);
channel->kernel_buf = NULL;
channel->dma_len = 0;
}
}
error_unlock_buffer:
mutex_unlock(&channel->buffer_mutex);
mutex_unlock(&channel->transfer_mutex);
return ret;
}
long calculet_dma_c2h_transfer(struct device *dev, struct calculet_dma_channel *channel, struct calculet_resource *resource)
{
int ch = channel->channel_index;
unsigned long timeout = msecs_to_jiffies(100000); // 设置超时时间为100秒
struct calculet_pcie_board *board = dev_get_drvdata(dev);
long ret = 0;
int transfer_result;
if (!board || !calculet_board_is_operational(board)) {
return -ENODEV;
}
CALCULET_PERF_START(c2h_transfer);
// 获取传输互斥锁
if (mutex_lock_interruptible(&channel->transfer_mutex)) {
return -ERESTARTSYS;
}
// 缓冲区管理
mutex_lock(&channel->buffer_mutex);
if(channel->dma_channel_size <= DMA_CHANNEL_MAX_SIZE) {
if(channel->kernel_buf == NULL) {
channel->kernel_buf = dma_alloc_coherent(dev, DMA_CHANNEL_MAX_SIZE, &channel->dma_handle, GFP_KERNEL);
if (!channel->kernel_buf) {
calculet_dev_log(CALCULET_LOG_ERROR, dev, "Failed to allocate DMA buffer");
ret = -ENOMEM;
goto error_unlock_buffer;
}
}
} else {
calculet_dev_log(CALCULET_LOG_ERROR, dev, "C2H transfer size %ld is too large", channel->dma_channel_size);
ret = -EINVAL;
goto error_unlock_buffer;
}
// if(channel->dma_len != channel->dma_channel_size) {
// if(channel->kernel_buf == NULL) {
// channel->kernel_buf = dma_alloc_coherent(dev, channel->dma_channel_size, &channel->dma_handle, GFP_KERNEL);
// if (!channel->kernel_buf) {
// calculet_dev_log(CALCULET_LOG_ERROR, dev, "Failed to allocate DMA buffer");
// ret = -ENOMEM;
// goto error_unlock_buffer;
// }
// } else {
// dma_free_coherent(dev, channel->dma_len, channel->kernel_buf, channel->dma_handle);
// channel->kernel_buf = dma_alloc_coherent(dev, channel->dma_channel_size, &channel->dma_handle, GFP_KERNEL);
// if (!channel->kernel_buf) {
// calculet_dev_log(CALCULET_LOG_ERROR, dev, "Failed to allocate DMA buffer");
// ret = -ENOMEM;
// goto error_unlock_buffer;
// }
// }
// channel->dma_len = channel->dma_channel_size;
// }
atomic_inc(&channel->buffer_users);
mutex_unlock(&channel->buffer_mutex);
// 配置DMA寄存器
iowrite32(0x10, (u8*)resource->address + DMA_WRCH_CONTROL1_OFF(ch));
iowrite32(PCI_DMA_L(channel->dma_channel_size), (u8*)resource->address + DMA_WRCH_SIZE_OFF(ch));
iowrite32(PCI_DMA_L(channel->dst_address), (u8*)resource->address + DMA_WRCH_SAR_LOW_OFF(ch));
iowrite32(PCI_DMA_H(channel->dst_address), (u8*)resource->address + DMA_WRCH_SAR_HIGH_OFF(ch));
iowrite32(PCI_DMA_L(channel->dma_handle), (u8*)resource->address + DMA_WRCH_DAR_LOW_OFF(ch));
iowrite32(PCI_DMA_H(channel->dma_handle), (u8*)resource->address + DMA_WRCH_DAR_HIGH_OFF(ch));
// 重置完成标志
atomic_set(&channel->transfer_finish, 0);
// 使用资源结构体中的锁
mutex_lock(&resource->dma_global_lock);
iowrite32(ch, (u8*)resource->address + DMA_WRITE_DOORBELL_OFF);
mutex_unlock(&resource->dma_global_lock);
// 等待传输完成或超时
transfer_result = wait_event_interruptible_timeout(channel->channel_wq,
atomic_read(&channel->transfer_finish) != 0,
timeout);
if (transfer_result > 0) {
int finish_status = atomic_read(&channel->transfer_finish);
if (finish_status == 1) {
// 传输成功,将数据从DMA一致性内存复制回用户空间
if (copy_to_user((void __user *)channel->buffer, channel->kernel_buf, channel->dma_channel_size)) {
calculet_dev_log(CALCULET_LOG_ERROR, dev, "Failed to copy_to_user");
calculet_board_stats_update_error(board);
calculet_dma_update_channel_stats(channel, 0, false);
ret = -EFAULT;
} else {
calculet_board_stats_update_dma_c2h(board, channel->dma_channel_size);
calculet_dma_update_channel_stats(channel, channel->dma_channel_size, true);
ret = 0;
}
} else {
// 传输错误
calculet_dev_log(CALCULET_LOG_ERROR, dev, "C2H transfer failed with status %d", finish_status);
calculet_board_stats_update_error(board);
calculet_dma_update_channel_stats(channel, 0, false);
ret = finish_status;
}
} else if (transfer_result == 0) {
// 超时
calculet_dev_log(CALCULET_LOG_ERROR, dev, "C2H transfer timed out");
calculet_board_stats_update_error(board);
calculet_dma_update_channel_stats(channel, 0, false);
ret = -ETIMEDOUT;
} else {
// 被信号中断
calculet_dev_log(CALCULET_LOG_ERROR, dev, "C2H transfer interrupted");
ret = -ERESTARTSYS;
}
CALCULET_PERF_END(c2h_transfer, "channel %d, size %ld, result %ld",
ch, channel->dma_channel_size, ret);
calculet_dev_log(CALCULET_LOG_DEBUG, dev, "C2H transfer: channel %d, src 0x%llx, dst 0x%llx, size %ld, result %ld",
ch, channel->dst_address, channel->dma_handle, channel->dma_channel_size, ret);
mutex_lock(&channel->buffer_mutex);
if (atomic_dec_and_test(&channel->buffer_users)) {
// 如果发生错误,释放缓冲区
if (ret != 0 && channel->kernel_buf) {
dma_free_coherent(dev, channel->dma_channel_size, channel->kernel_buf, channel->dma_handle);
channel->kernel_buf = NULL;
channel->dma_len = 0;
}
}
error_unlock_buffer:
mutex_unlock(&channel->buffer_mutex);
mutex_unlock(&channel->transfer_mutex);
return ret;
}
irqreturn_t calculet_irqhandler(int irq, void *dev_id)
{
irqreturn_t return_value = IRQ_HANDLED;
struct calculet_dma_channel *channel = (struct calculet_dma_channel *)dev_id;
struct calculet_pcie_board *board;
if (!channel) {
return IRQ_NONE;
}
calculet_log(CALCULET_LOG_DEBUG, "IRQ %d for channel %d (vector %d)",
irq, channel->channel_index, channel->vector_index);
// 设置传输完成标志
atomic_set(&channel->transfer_finish, 1);
// 唤醒等待的线程
wake_up_interruptible(&channel->channel_wq);
// 更新中断统计(简化版本)
board = container_of(channel, struct calculet_pcie_board, cdma.dma_rdch_s[channel->channel_index]);
if (!board) {
board = container_of(channel, struct calculet_pcie_board, cdma.dma_wrch_s[channel->channel_index]);
}
if (board) {
calculet_board_stats_update_interrupt(board);
}
return return_value;
}
int calculet_poll_handler(void *data)
{
struct calculet_dma_controller *controller = (struct calculet_dma_controller *)data;
struct calculet_dma_poll *poll = controller->dma_kthread->dma_poll;
int region_data = 0;
int i;
atomic_set(&poll->poll_active, 1);
while(!kthread_should_stop() && !atomic_read(&controller->dma_kthread->should_stop))
{
spin_lock(&poll->poll_lock);
region_data = *poll->status_region;
if(region_data != 0) {
for(i = 0; i < CALCULET_TOTAL_CHANNELS; i++)
{
if(poll->channel_data[i] == region_data) {
if(i < CALCULET_TOTAL_CHANNELS/2) {
atomic_set(&controller->dma_rdch_s[i].transfer_finish, 1);
wake_up_interruptible(&controller->dma_rdch_s[i].channel_wq);
break;
}
else {
atomic_set(&controller->dma_wrch_s[i - CALCULET_READ_CHANNELS].transfer_finish, 1);
wake_up_interruptible(&controller->dma_wrch_s[i - CALCULET_READ_CHANNELS].channel_wq);
break;
}
}
}
*poll->status_region = 0;
}
spin_unlock(&poll->poll_lock);
schedule();
}
atomic_set(&poll->poll_active, 0);
complete(&controller->dma_kthread->thread_completion);
calculet_log(CALCULET_LOG_DEBUG, "Poll handler thread exiting");
return 0;
}
// 统计信息更新(简化版本)
void calculet_dma_update_channel_stats(struct calculet_dma_channel *channel,
size_t bytes, bool success)
{
if (!channel) {
return;
}
atomic64_inc(&channel->transfer_count);
if (success) {
atomic64_add(bytes, &channel->transfer_bytes);
}
channel->last_used = ktime_get();
}
// 控制器状态转储
void calculet_dma_dump_controller_state(struct calculet_dma_controller *controller)
{
int i;
if (!controller) {
return;
}
calculet_log(CALCULET_LOG_DEBUG, "DMA Controller State Dump");
calculet_log(CALCULET_LOG_DEBUG, " Enabled: %s", controller->controller_enabled ? "YES" : "NO");
calculet_log(CALCULET_LOG_DEBUG, " Active Transfers: %d", atomic_read(&controller->active_transfers));
calculet_log(CALCULET_LOG_DEBUG, " Read Channels Bitmap: 0x%lx", controller->read_channels_bitmap);
calculet_log(CALCULET_LOG_DEBUG, " Write Channels Bitmap: 0x%lx", controller->write_channels_bitmap);
// 转储读通道状态
calculet_log(CALCULET_LOG_DEBUG, " Read Channels:");
for (i = 0; i < CALCULET_READ_CHANNELS; i++) {
struct calculet_dma_channel *ch = &controller->dma_rdch_s[i];
calculet_log(CALCULET_LOG_DEBUG, " CH%d: state=%d, transfers=%lld, bytes=%lld",
i, calculet_dma_channel_get_state(ch),
atomic64_read(&ch->transfer_count),
atomic64_read(&ch->transfer_bytes));
}
// 转储写通道状态
calculet_log(CALCULET_LOG_DEBUG, " Write Channels:");
for (i = 0; i < CALCULET_WRITE_CHANNELS; i++) {
struct calculet_dma_channel *ch = &controller->dma_wrch_s[i];
calculet_log(CALCULET_LOG_DEBUG, " CH%d: state=%d, transfers=%lld, bytes=%lld",
i, calculet_dma_channel_get_state(ch),
atomic64_read(&ch->transfer_count),
atomic64_read(&ch->transfer_bytes));
}
}
@@ -0,0 +1,220 @@
// SPDX-License-Identifier: GPL-2.0
/**
* Copyright (c) 2024-2025 CALCULET Technologies Ltd. All rights reserved.
*/
#ifndef _CALCULET_DMA_H_
#define _CALCULET_DMA_H_
#include "ioctl.h"
#include <linux/dma-mapping.h>
#include <linux/types.h>
#include <linux/mutex.h>
#include <linux/kthread.h>
#include <linux/wait.h>
#include <linux/scatterlist.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/ktime.h>
#include <linux/atomic.h>
#include <linux/spinlock.h>
#include <linux/completion.h>
#define DESC_MAGIC 0x0UL
#define DESC_MAX_COUNT (1024)
#define DMA_TRANSFER_MAX_DESC (2048)
#define DMA_CHANNEL_MAX_SIZE (8 * 1024 * 1024)
/* 获取64位地址的高32位和低32位 */
#define PCI_DMA_H(addr) ((addr >> 16) >> 16)
#define PCI_DMA_L(addr) (addr & 0xffffffffUL)
// DMA寄存器偏移定义
#define DMA_CHANNEL_BASE_OFF(i) ((i) * 2 * 0x100)
#define DMA_WRITE_ENGINE_EN_OFF 0xc
#define DMA_WRITE_DOORBELL_OFF 0x10
#define DMA_WRCH_CONTROL1_OFF(i) (DMA_CHANNEL_BASE_OFF(i) + 0x200)
#define DMA_WRCH_SIZE_OFF(i) (DMA_CHANNEL_BASE_OFF(i) + 0x208)
#define DMA_WRCH_SAR_LOW_OFF(i) (DMA_CHANNEL_BASE_OFF(i) + 0x20c)
#define DMA_WRCH_SAR_HIGH_OFF(i) (DMA_CHANNEL_BASE_OFF(i) + 0x210)
#define DMA_WRCH_DAR_LOW_OFF(i) (DMA_CHANNEL_BASE_OFF(i) + 0x214)
#define DMA_WRCH_DAR_HIGH_OFF(i) (DMA_CHANNEL_BASE_OFF(i) + 0x218)
#define DMA_READ_ENGINE_EN_OFF 0x2c
#define DMA_READ_DOORBELL_OFF 0x30
#define DMA_RDCH_CONTROL1_OFF(i) (DMA_CHANNEL_BASE_OFF(i) + 0x300)
#define DMA_RDCH_SIZE_OFF(i) (DMA_CHANNEL_BASE_OFF(i) + 0x308)
#define DMA_RDCH_SAR_LOW_OFF(i) (DMA_CHANNEL_BASE_OFF(i) + 0x30c)
#define DMA_RDCH_SAR_HIGH_OFF(i) (DMA_CHANNEL_BASE_OFF(i) + 0x310)
#define DMA_RDCH_DAR_LOW_OFF(i) (DMA_CHANNEL_BASE_OFF(i) + 0x314)
#define DMA_RDCH_DAR_HIGH_OFF(i) (DMA_CHANNEL_BASE_OFF(i) + 0x318)
// 前向声明
struct calculet_resource;
// DMA通道状态枚举
enum calculet_dma_channel_state {
DMA_CHANNEL_IDLE = 0,
DMA_CHANNEL_BUSY,
DMA_CHANNEL_COMPLETING,
DMA_CHANNEL_ERROR,
DMA_CHANNEL_DISABLED
};
// DMA IO回调结构体
struct calculet_dma_io_cb {
void __user *buf;
size_t len;
unsigned int pages_nr;
struct sg_table sgt;
struct page **pages;
};
// DMA传输上下文
struct calculet_dma_transfer_context {
struct completion completion;
int result;
ktime_t start_time;
ktime_t end_time;
pid_t requester_pid;
atomic_t ref_count;
};
// DMA通道结构体
struct calculet_dma_channel {
u8 channel_index;
u8 vector_index;
int msi_irq_line;
u32 irq_bitmask;
// 改进的状态管理
enum calculet_dma_channel_state state;
atomic_t transfer_finish;
spinlock_t state_lock;
resource_size_t dma_regs;
size_t dma_channel_size;
uint8_t *buffer;
uint64_t dst_address;
wait_queue_head_t channel_wq;
struct calculet_dma_io_cb *desc_io_cb;
// 改进的缓冲区管理
void *kernel_buf;
size_t dma_len;
dma_addr_t dma_handle;
struct mutex buffer_mutex;
atomic_t buffer_users;
// 传输统计和监控
atomic64_t transfer_count;
atomic64_t transfer_bytes;
ktime_t last_used;
// 当前传输上下文
struct calculet_dma_transfer_context *current_transfer;
struct mutex transfer_mutex;
};
// DMA轮询结构体
struct calculet_dma_poll {
dma_addr_t status_region_phys;
uint32_t *status_region;
uint32_t channel_status[CALCULET_TOTAL_CHANNELS];
uint32_t channel_data[CALCULET_TOTAL_CHANNELS];
spinlock_t poll_lock;
atomic_t poll_active;
};
// DMA内核线程结构体
struct calculet_dma_kthread {
struct calculet_dma_poll *dma_poll;
struct task_struct *poll_thread;
u32 poll_interval_ms;
char thread_name[24];
atomic_t should_stop;
struct completion thread_completion;
};
// DMA控制器结构体
struct calculet_dma_controller {
struct device *dev;
struct calculet_dma_channel dma_rdch_s[CALCULET_READ_CHANNELS];
struct calculet_dma_channel dma_wrch_s[CALCULET_WRITE_CHANNELS];
struct calculet_dma_channel msi_req[CALCULET_TOTAL_CHANNELS];
// struct calculet_dma_channel soft_reset;
struct calculet_dma_kthread *dma_kthread;
// 控制器级别的锁和状态
struct mutex controller_mutex;
atomic_t active_transfers;
bool controller_enabled;
// 通道分配管理
unsigned long read_channels_bitmap; // 读通道分配位图
unsigned long write_channels_bitmap; // 写通道分配位图
struct mutex allocation_mutex;
};
// 通道分配和释放
int calculet_dma_allocate_channel(struct calculet_dma_controller *controller,
enum calculet_transfer_direction direction,
int *channel_id);
void calculet_dma_release_channel(struct calculet_dma_controller *controller,
enum calculet_transfer_direction direction,
int channel_id);
// DMA通道状态管理
void calculet_dma_channel_set_state(struct calculet_dma_channel *channel,
enum calculet_dma_channel_state state);
enum calculet_dma_channel_state calculet_dma_channel_get_state(struct calculet_dma_channel *channel);
bool calculet_dma_channel_is_available(struct calculet_dma_channel *channel);
// 传输上下文管理
struct calculet_dma_transfer_context* calculet_dma_create_transfer_context(pid_t pid);
void calculet_dma_destroy_transfer_context(struct calculet_dma_transfer_context *ctx);
void calculet_dma_transfer_context_get(struct calculet_dma_transfer_context *ctx);
void calculet_dma_transfer_context_put(struct calculet_dma_transfer_context *ctx);
// 函数声明
int calculet_dma_controller_init(struct calculet_dma_controller *controller, struct device *dev, bool poll_en);
void calculet_dma_controller_deinit(struct calculet_dma_controller *controller, bool poll_en);
long calculet_dma_ioctl(struct calculet_resource *resource, struct calculet_dma_controller *controller, unsigned int cmd, unsigned long arg);
int calculet_dma_poll_ioctl(struct calculet_dma_controller *controller);
// DMA传输函数(改进版本)
long calculet_dma_h2c_transfer(struct device *dev, struct calculet_dma_channel *channel, struct calculet_resource *resource);
long calculet_dma_c2h_transfer(struct device *dev, struct calculet_dma_channel *channel, struct calculet_resource *resource);
// 新增:安全的DMA传输函数
long calculet_dma_safe_transfer(struct calculet_dma_controller *controller,
struct calculet_dma_transfer_channels_params *params,
struct calculet_resource *resource);
// 超时处理
void calculet_dma_timeout_handler(struct timer_list *timer);
int calculet_dma_set_timeout(struct calculet_dma_channel *channel, unsigned int timeout_ms);
// 中断处理函数
irqreturn_t calculet_irqhandler(int irq, void* dev_id);
int calculet_poll_handler(void *data);
// 错误恢复
void calculet_dma_error_recovery_work(struct work_struct *work);
int calculet_dma_channel_recovery(struct calculet_dma_channel *channel);
// 统计和监控
void calculet_dma_update_channel_stats(struct calculet_dma_channel *channel,
size_t bytes, bool success);
void calculet_dma_dump_channel_stats(struct calculet_dma_controller *controller);
// 调试和诊断
int calculet_dma_validate_transfer_params(struct calculet_dma_transfer_channels_params *params);
void calculet_dma_dump_controller_state(struct calculet_dma_controller *controller);
#endif /* _CALCULET_DMA_H_ */
@@ -0,0 +1,645 @@
// SPDX-License-Identifier: GPL-2.0
/**
* Copyright (c) 2024-2025 CALCULET Technologies Ltd. All rights reserved.
*/
#include <linux/version.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/pagemap.h>
#include <linux/uaccess.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/kref.h>
#include <linux/crc32.h>
#include <linux/hash.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
#include <linux/sched/signal.h>
#endif
#include "fops.h"
#include "proc.h"
#include "calculet_pci_core.h"
#define DRIVER_NAME "calculet"
#define CALCULET_SOFT_RESET_REG 0x2238108
// 简化的文件上下文
struct calculet_file_private {
struct calculet_pcie_board *board;
pid_t owner_pid;
bool is_valid;
struct kref ref_count;
};
// 自定义混淆函数
static u32 calculet_hash_mix(u32 input, u32 salt)
{
input ^= salt;
input = ((input >> 16) ^ input) * 0x45d9f3b;
input = ((input >> 16) ^ input) * 0x45d9f3b;
input = (input >> 16) ^ input;
return input;
}
void calculet_generate_simple_uuid(const char *serial_number, char *uuid_str)
{
u32 seeds[4];
u8 uuid_bytes[16];
u32 base_hash;
int i;
// 计算基础哈希
base_hash = crc32(0, serial_number, strlen(serial_number));
// 生成4个种子值
seeds[0] = calculet_hash_mix(base_hash, 0x12345678);
seeds[1] = calculet_hash_mix(base_hash, 0x9ABCDEF0);
seeds[2] = calculet_hash_mix(base_hash, 0xFEDCBA98);
seeds[3] = calculet_hash_mix(base_hash, 0x76543210);
// 转换为字节
for (i = 0; i < 4; i++) {
uuid_bytes[i*4] = (seeds[i] >> 24) & 0xFF;
uuid_bytes[i*4+1] = (seeds[i] >> 16) & 0xFF;
uuid_bytes[i*4+2] = (seeds[i] >> 8) & 0xFF;
uuid_bytes[i*4+3] = seeds[i] & 0xFF;
}
// 设置UUID版本和变体位
uuid_bytes[6] = (uuid_bytes[6] & 0x0F) | 0x40; // 版本4
uuid_bytes[8] = (uuid_bytes[8] & 0x3F) | 0x80; // 变体位
// 格式化为UUID字符串
snprintf(uuid_str, 37, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
uuid_bytes[0], uuid_bytes[1], uuid_bytes[2], uuid_bytes[3],
uuid_bytes[4], uuid_bytes[5], uuid_bytes[6], uuid_bytes[7],
uuid_bytes[8], uuid_bytes[9], uuid_bytes[10], uuid_bytes[11],
uuid_bytes[12], uuid_bytes[13], uuid_bytes[14], uuid_bytes[15]);
}
// 文件上下文释放函数
static void calculet_file_private_release(struct kref *ref)
{
struct calculet_file_private *priv = container_of(ref, struct calculet_file_private, ref_count);
if (priv->board) {
calculet_board_mgr_put_board(priv->board);
}
kfree(priv);
}
static void calculet_file_private_get(struct calculet_file_private *priv)
{
if (priv) {
kref_get(&priv->ref_count);
}
}
static void calculet_file_private_put(struct calculet_file_private *priv)
{
if (priv) {
kref_put(&priv->ref_count, calculet_file_private_release);
}
}
int calculet_pcie_fops_open(struct inode *inode, struct file *filp)
{
u32 major = MAJOR(inode->i_rdev);
u32 minor = MINOR(inode->i_rdev);
struct calculet_pcie_board *board;
struct calculet_file_private *priv;
int err = 0;
calculet_log(CALCULET_LOG_DEBUG, "Device open: PID %d, major %d, minor %d",
current->pid, major, minor);
// 分配文件私有数据
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) {
return -ENOMEM;
}
// 初始化文件私有数据
kref_init(&priv->ref_count);
priv->owner_pid = current->pid;
priv->is_valid = true;
// 获取板卡引用
board = calculet_board_mgr_get_board(minor);
if (!board) {
calculet_log(CALCULET_LOG_ERROR, "PCIe board not found for /dev/calculet%d", minor);
err = -ENODEV;
goto error_free_priv;
}
// 检查板卡状态
if (!calculet_board_is_operational(board)) {
calculet_log(CALCULET_LOG_ERROR, "Board %d is not operational", minor);
err = -ENODEV;
goto error_put_board;
}
priv->board = board;
// 添加进程监测
err = calculet_proc_add_process(board, current);
if (err) {
calculet_log(CALCULET_LOG_WARN, "Failed to add process monitoring for PID %d: %d",
current->pid, err);
}
filp->private_data = priv;
calculet_board_log(CALCULET_LOG_DEBUG, board, "Device opened by process %s (PID: %d)",
current->comm, current->pid);
return 0;
error_put_board:
calculet_board_mgr_put_board(board);
error_free_priv:
kfree(priv);
return err;
}
int calculet_pcie_fops_release(struct inode *inode, struct file *filp)
{
struct calculet_file_private *priv = (struct calculet_file_private *)filp->private_data;
if (!priv) {
return 0;
}
// 标记为无效,防止新的操作
priv->is_valid = false;
if (priv->board) {
// 更新进程状态为已关闭
calculet_proc_remove_process(priv->board, priv->owner_pid);
calculet_board_log(CALCULET_LOG_DEBUG, priv->board,
"Device closed by process PID %d", priv->owner_pid);
}
// 释放文件私有数据
calculet_file_private_put(priv);
filp->private_data = NULL;
return 0;
}
static int calculet_msi_ioctl(struct device *dev, struct calculet_dma_controller *controller, unsigned long arg)
{
struct calculet_msi_req *req_num = kmalloc(sizeof(*req_num), GFP_KERNEL);
unsigned long timeout = msecs_to_jiffies(30000); // 30秒超时
struct calculet_dma_channel *channel;
if (!dev || !controller) {
return -EINVAL;
}
if (copy_from_user(req_num, (void __user*)arg, sizeof(*req_num))) {
calculet_dev_log(CALCULET_LOG_ERROR, dev, "copy_from_user fail");
kfree(req_num);
return -EFAULT;
}
channel = &controller->msi_req[req_num->msi_req];
if (wait_event_interruptible_timeout(channel->channel_wq,
atomic_read(&channel->transfer_finish) == 1,
timeout) > 0) {
atomic_set(&channel->transfer_finish, 0);
kfree(req_num);
calculet_dev_log(CALCULET_LOG_DEBUG, dev, "EP MSI request completed successfully");
return 0;
} else {
calculet_dev_log(CALCULET_LOG_ERROR, dev, "EP MSI request timed out");
kfree(req_num);
return -ETIMEDOUT;
}
}
/**
* PCIe软复位主函数
*/
static int calculet_pcie_soft_reset(struct calculet_pcie_board *board)
{
struct calculet_resource *resource = &board->pcie_resources.rt_registers;
struct pci_dev *pdev = board->pDev;
struct pci_bus *bus = pdev->bus;
int ret;
calculet_board_log(CALCULET_LOG_DEBUG, board, "Starting PCIe soft reset");
if (!board || !pdev) {
calculet_log(CALCULET_LOG_ERROR, "Invalid board or device pointer");
return -EINVAL;
}
// 准备全局软复位
ret = calculet_global_soft_reset_prepare(board);
if (ret) {
return ret;
}
/* 1. 触发设备端复位 */
calculet_resource_write32(resource, CALCULET_SOFT_RESET_REG, 1);
/* 2. 等待复位开始 */
msleep(100000); // 减少等待时间
pci_lock_rescan_remove();
pci_stop_and_remove_bus_device(pdev);
pci_rescan_bus(bus->parent);
pci_unlock_rescan_remove();
return 0;
}
static long calculet_get_process_list_ioctl(struct calculet_pcie_board *board, unsigned long arg)
{
int i = 0;
struct calculet_process_info *proc_info;
struct calculet_process_info_list_user *user_list = kmalloc(sizeof(*user_list), GFP_KERNEL);
if (!user_list) {
return -ENOMEM;
}
if (!board || !calculet_board_is_operational(board)) {
kfree(user_list);
return -ENODEV;
}
if (copy_from_user(user_list, (void __user*)arg, sizeof(*user_list))) {
calculet_board_log(CALCULET_LOG_ERROR, board, "copy_from_user fail");
kfree(user_list);
return -EFAULT;
}
// 清零并初始化
memset(user_list, 0, sizeof(*user_list));
user_list->board_index = board->board_index;
mutex_lock(&board->process_list_mutex);
list_for_each_entry(proc_info, &board->process_list, list) {
if (proc_info->is_active && i < MAX_PROCESS_ENTRIES) {
user_list->processes[i].pid = proc_info->pid;
user_list->processes[i].ppid = proc_info->ppid;
user_list->processes[i].uid = proc_info->uid;
strncpy(user_list->processes[i].comm, proc_info->comm, sizeof(user_list->processes[i].comm) - 1);
user_list->processes[i].comm[sizeof(user_list->processes[i].comm) - 1] = '\0';
user_list->processes[i].is_active = proc_info->is_active ? 1 : 0;
i++;
}
}
mutex_unlock(&board->process_list_mutex);
user_list->count = i; // 更新实际数量
if (copy_to_user((void __user*)arg, user_list, sizeof(*user_list))) {
calculet_board_log(CALCULET_LOG_ERROR, board, "copy_to_user fail");
kfree(user_list);
return -EFAULT;
}
kfree(user_list);
calculet_board_log(CALCULET_LOG_DEBUG, board, "Returned %d active processes", i);
return 0;
}
static long calculet_get_board_info_ioctl(struct calculet_pcie_board *board, unsigned long arg)
{
u16 lnksta;
u64 memory_band;
struct calculet_ioc_info *board_info = kmalloc(sizeof(struct calculet_ioc_info), GFP_KERNEL);
if (!board_info) {
return -ENOMEM;
}
if (!board || !calculet_board_is_operational(board)) {
kfree(board_info);
return -ENODEV;
}
if (copy_from_user((void *)board_info, (void __user*)arg, sizeof(struct calculet_ioc_info))) {
calculet_board_log(CALCULET_LOG_ERROR, board, "copy_from_user fail");
kfree(board_info);
return -EFAULT;
}
pcie_capability_read_word(board->pDev, PCI_EXP_LNKSTA, &lnksta);
// 解析速率
board_info->pci_info.link_speed = lnksta & PCI_EXP_LNKSTA_CLS;
// 解析通道宽度
board_info->pci_info.link_width = (lnksta & PCI_EXP_LNKSTA_NLW) >> PCI_EXP_LNKSTA_NLW_SHIFT;
board_info->vendor_id = board->pDev->vendor;
board_info->device_id = board->pDev->device;
board_info->subsystem_vendor = board->pDev->subsystem_vendor;
board_info->subsystem_device = board->pDev->subsystem_device;
board_info->pci_info.domain = pci_domain_nr(board->pDev->bus);
board_info->pci_info.bus = board->pDev->bus->number;
board_info->pci_info.device = PCI_SLOT(board->pDev->devfn);
board_info->pci_info.function = PCI_FUNC(board->pDev->devfn);
// 从bar4读取硬件规格参数
calculet_read_strings(board_info->product_name, &board->pcie_resources.smi_registers, CALCULET_PRODUCT_NAME_REG, 32);
calculet_read_strings(board_info->serial_number, &board->pcie_resources.smi_registers, CALCULET_SERIAL_NUMBER_REG, 32);
calculet_read_strings(board_info->product_number, &board->pcie_resources.smi_registers, CALCULET_PRODUCT_NUMBER_REG, 32);
calculet_read_strings(board_info->firmware_version, &board->pcie_resources.smi_registers, CALCULET_FIRMWARE_VERSION_REG, 12);
calculet_generate_simple_uuid(board_info->serial_number, board_info->calculet_uuid);
// 总显存大小 (0x004C, 8字节)
board_info->total_memory = calculet_resource_read64(&board->pcie_resources.smi_registers, CALCULET_TOTAL_MEMORY_REG);
// 显存带宽 (0x0054, 8字节)
memory_band = calculet_resource_read64(&board->pcie_resources.smi_registers, CALCULET_MEMORY_BANDWIDTH_REG);
memcpy(&board_info->memory_bandwidth, &memory_band, sizeof(double));
// DDR频率 (0x005C, 4字节)
board_info->ddr_frequency = calculet_resource_read32(&board->pcie_resources.smi_registers, CALCULET_DDR_FREQUENCY_REG);
// 板卡 TDP上限 (0x0060, 4字节)
board_info->max_power_limit = calculet_resource_read32(&board->pcie_resources.smi_registers, CALCULET_MAX_POWER_LIMIT_REG);
// KS1温度上限 (0x0064, 4字节)
board_info->temp_limit_ks1 = calculet_resource_read32(&board->pcie_resources.smi_registers, CALCULET_TEMP_LIMIT_REG);
board_info->driver_version.major = CALCULET_DRV_VER_MAJOR;
board_info->driver_version.minor = CALCULET_DRV_VER_MINOR;
snprintf(board_info->driver_version.string, KS1_MAX_STRING_LEN,
"%s", CALCULET_DRV_VER);
if (copy_to_user((void __user*)arg, (void *)board_info, sizeof(struct calculet_ioc_info))) {
calculet_board_log(CALCULET_LOG_ERROR, board, "copy_to_user fail");
kfree(board_info);
return -EFAULT;
}
kfree(board_info);
return 0;
}
static long calculet_bar64_transfer_ioctl(struct calculet_pcie_board *board, unsigned long arg)
{
long err = 0;
struct calculet_bar64_transfer_params transfer;
if (!board || !calculet_board_is_operational(board)) {
return -ENODEV;
}
if (copy_from_user(&transfer, (void __user*)arg, sizeof(transfer))) {
calculet_board_log(CALCULET_LOG_ERROR, board, "copy_from_user fail");
return -EFAULT;
}
err = calculet_pcie_bar64_transfer(&board->pcie_resources, &transfer);
if (err < 0) {
calculet_board_log(CALCULET_LOG_ERROR, board, "bar transfer failed %ld", err);
calculet_board_stats_update_error(board);
}
if (copy_to_user((void __user*)arg, &transfer, sizeof(transfer))) {
calculet_board_log(CALCULET_LOG_ERROR, board, "copy_to_user fail");
return -EFAULT;
}
return err;
}
static long calculet_bar32_transfer_ioctl(struct calculet_pcie_board *board, unsigned long arg)
{
long err = 0;
struct calculet_bar32_transfer_params transfer;
if (!board || !calculet_board_is_operational(board)) {
return -ENODEV;
}
if (copy_from_user(&transfer, (void __user*)arg, sizeof(transfer))) {
calculet_board_log(CALCULET_LOG_ERROR, board, "copy_from_user fail");
return -EFAULT;
}
err = calculet_pcie_bar32_transfer(&board->pcie_resources, &transfer);
if (err < 0) {
calculet_board_log(CALCULET_LOG_ERROR, board, "bar32 transfer failed %ld", err);
calculet_board_stats_update_error(board);
}
if (copy_to_user((void __user*)arg, &transfer, sizeof(transfer))) {
calculet_board_log(CALCULET_LOG_ERROR, board, "copy_to_user fail");
return -EFAULT;
}
return err;
}
static long calculet_get_sn_ioctl(struct calculet_pcie_board *board, unsigned long arg)
{
struct calculet_sn_info sn_info;
if (!board || !calculet_board_is_operational(board)) {
return -ENODEV;
}
if (copy_from_user(&sn_info, (void __user*)arg, sizeof(sn_info))) {
calculet_board_log(CALCULET_LOG_ERROR, board, "copy_from_user fail");
return -EFAULT;
}
calculet_read_strings(sn_info.sn, &board->pcie_resources.smi_registers, CALCULET_SERIAL_NUMBER_REG + sn_info.chip_id * 1024, 32);
if (copy_to_user((void __user*)arg, &sn_info, sizeof(sn_info))) {
calculet_board_log(CALCULET_LOG_ERROR, board, "copy_to_user fail");
return -EFAULT;
}
return 0;
}
static long calculet_general_ioctl(struct calculet_pcie_board *board, unsigned int cmd, unsigned long arg)
{
if (!board || !calculet_board_is_operational(board)) {
return -ENODEV;
}
switch (cmd) {
case CALCULET_BOARD_INFO:
return calculet_get_board_info_ioctl(board, arg);
case CALCULET_BAR64_TRANSFER:
return calculet_bar64_transfer_ioctl(board, arg);
case CALCULET_BAR32_TRANSFER:
return calculet_bar32_transfer_ioctl(board, arg);
case CALCULET_GET_SN:
return calculet_get_sn_ioctl(board, arg);
default:
calculet_board_log(CALCULET_LOG_ERROR, board, "Invalid general ioctl code 0x%x (nr: %d)",
cmd, _IOC_NR(cmd));
return -ENOTTY;
}
}
static long calculet_ep_ioctl(struct calculet_pcie_board *board, unsigned int cmd, unsigned long arg)
{
if (!board || !calculet_board_is_operational(board)) {
return -ENODEV;
}
switch (cmd) {
case CALCULET_EP_MSI_REQ:
return calculet_msi_ioctl(&board->pDev->dev, &board->cdma, arg);
case CALCULET_SOFT_RESET:
return calculet_pcie_soft_reset(board);
default:
calculet_board_log(CALCULET_LOG_ERROR, board, "Invalid ep ioctl code 0x%x (nr: %d)",
cmd, _IOC_NR(cmd));
return -ENOTTY;
}
}
static long calculet_proc_ioctl(struct calculet_pcie_board *board, unsigned int cmd, unsigned long arg)
{
if (!board || !calculet_board_is_operational(board)) {
return -ENODEV;
}
switch (cmd) {
case CALCULET_GET_PROCESS_LIST:
return calculet_get_process_list_ioctl(board, arg);
default:
calculet_board_log(CALCULET_LOG_ERROR, board, "Invalid proc ioctl code 0x%x (nr: %d)",
cmd, _IOC_NR(cmd));
return -ENOTTY;
}
}
long calculet_pcie_fops_unlockedioctl(struct file* filp, unsigned int cmd, unsigned long arg)
{
long err = 0;
struct calculet_file_private *priv = (struct calculet_file_private*)filp->private_data;
struct calculet_pcie_board *board;
if (!priv || !priv->is_valid) {
calculet_log(CALCULET_LOG_ERROR, "Invalid file private data");
return -ENODEV;
}
board = priv->board;
if (!board || !board->pDev) {
calculet_log(CALCULET_LOG_ERROR, "Board is NULL or invalid");
return -ENODEV;
}
// 获取文件私有数据引用
calculet_file_private_get(priv);
// 再次检查板卡状态
if (!calculet_board_is_operational(board)) {
err = -ENODEV;
goto out;
}
// 更新进程访问记录
calculet_proc_update_process_access(board, current->pid);
switch (_IOC_TYPE(cmd)) {
case CALCULET_GENERAL_IOCTL_MAGIC:
err = calculet_general_ioctl(board, cmd, arg);
break;
case CALCULET_DMA_IOCTL_MAGIC:
err = calculet_dma_ioctl(&board->pcie_resources.dma_registers, &board->cdma, cmd, arg);
break;
case CALCULET_PCI_EP_IOCTL_MAGIC:
err = calculet_ep_ioctl(board, cmd, arg);
break;
case CALCULET_PROC_IOCTL_MAGIC:
err = calculet_proc_ioctl(board, cmd, arg);
break;
default:
calculet_board_log(CALCULET_LOG_ERROR, board, "Invalid ioctl type %d", _IOC_TYPE(cmd));
err = -ENOTTY;
}
out:
// 释放文件私有数据引用
calculet_file_private_put(priv);
return err;
}
int calculet_pcie_fops_mmap(struct file* filp, struct vm_area_struct *vma)
{
struct calculet_file_private *priv = (struct calculet_file_private*)filp->private_data;
struct calculet_pcie_board *board;
struct calculet_resource *resource;
unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
unsigned long size = vma->vm_end - vma->vm_start;
unsigned long pfn;
int bar_num = -1;
if (!priv || !priv->is_valid) {
return -ENODEV;
}
board = priv->board;
if (!board || !calculet_board_is_operational(board)) {
return -ENODEV;
}
calculet_board_log(CALCULET_LOG_DEBUG, board, "mmap called by PID %d", current->pid);
// 根据offset判断映射哪个空间
if (offset == MMAP_BAR2_OFFSET) {
resource = &board->pcie_resources.rt_registers;
bar_num = 2;
size = resource->size;
pfn = resource->phy_address >> PAGE_SHIFT;
// BAR空间:不可缓存
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
if (io_remap_pfn_range(vma, vma->vm_start, pfn,
size, vma->vm_page_prot))
return -EAGAIN;
} else if (offset == MMAP_BAR4_OFFSET) {
bar_num = 4;
size = resource->size;
pfn = resource->phy_address >> PAGE_SHIFT;
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
if (io_remap_pfn_range(vma, vma->vm_start, pfn,
size, vma->vm_page_prot))
return -EAGAIN;
} else if (offset == MMAP_DMA_OFFSET) {
// // DMA buffer映射(这是系统内存,不是IO空间)
// if (size > pdev->dma_size)
// return -EINVAL;
// // DMA内存可以使用write-combine优化
// vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
// // 使用dma_mmap_coherent映射DMA内存
// if (dma_mmap_coherent(&pdev->pci_dev->dev, vma,
// pdev->dma_virt, pdev->dma_handle,
// pdev->dma_size))
// return -EAGAIN;
} else {
printk(KERN_ERR "Invalid mmap offset: 0x%lx\n", offset);
return -EINVAL;
}
printk(KERN_INFO "mmap: offset=0x%lx, size=%lu, bar=%d\n",
offset, size, bar_num);
return 0;
}
@@ -0,0 +1,39 @@
// SPDX-License-Identifier: GPL-2.0
/**
* Copyright (c) 2024-2025 CALCULET Technologies Ltd. All rights reserved.
*/
#ifndef _CALCULET_FOPS_H_
#define _CALCULET_FOPS_H_
#include <linux/fs.h>
#include <linux/interrupt.h>
// 定义offset映射规则
#define MMAP_BAR2_OFFSET 0x10000000
#define MMAP_BAR4_OFFSET 0x20000000
#define MMAP_DMA_OFFSET 0x30000000
/* 为了兼容性,保留原有的一些常量定义 */
#define RESET_COMPLETION_TIMEOUT_MS 5000 // 减少到5秒
#define MAX_RESET_RETRY 100 // 增加重试次数但减少单次延时
#define RETRY_DELAY_US 1000 // 减少到1ms
// calculet ioc info bar4
#define CALCULET_PRODUCT_NAME_REG 0x0000
#define CALCULET_SERIAL_NUMBER_REG 0x4800
#define CALCULET_PRODUCT_NUMBER_REG 0x002C
#define CALCULET_FIRMWARE_VERSION_REG 0x0020
#define CALCULET_TOTAL_MEMORY_REG 0x004C
#define CALCULET_MEMORY_BANDWIDTH_REG 0x0054
#define CALCULET_DDR_FREQUENCY_REG 0x005C
#define CALCULET_MAX_POWER_LIMIT_REG 0x0060
#define CALCULET_TEMP_LIMIT_REG 0x0064
int calculet_pcie_fops_open(struct inode* inode, struct file* filp);
int calculet_pcie_fops_release(struct inode* inode, struct file* filp);
long calculet_pcie_fops_unlockedioctl(struct file* filp, unsigned int cmd, unsigned long arg);
int calculet_pcie_fops_mmap(struct file* filp, struct vm_area_struct *vma);
#endif /* _CALCULET_FOPS_H_ */
@@ -0,0 +1,207 @@
// SPDX-License-Identifier: GPL-2.0
/**
* Copyright (c) 2024-2025 CALCULET Technologies Ltd. All rights reserved.
*/
#ifndef _CALCULET_IOCTL_H_
#define _CALCULET_IOCTL_H_
#define CALCULET_DRV_VER_MAJOR 1
#define CALCULET_DRV_VER_MINOR 0
#define CALCULET_DRV_VER_REVISION 0
#define _STRINGIFY_EXPANDED( x ) #x
#define _STRINGIFY_NUMBER( x ) _STRINGIFY_EXPANDED(x)
#define CALCULET_DRV_VER _STRINGIFY_NUMBER(CALCULET_DRV_VER_MAJOR) "." _STRINGIFY_NUMBER(CALCULET_DRV_VER_MINOR) "." _STRINGIFY_NUMBER(CALCULET_DRV_VER_REVISION)
#define CALCULET_READ_CHANNELS 8
#define CALCULET_WRITE_CHANNELS 8
#define CALCULET_TOTAL_CHANNELS (CALCULET_READ_CHANNELS + CALCULET_WRITE_CHANNELS)
#define CALCULET_MAX_CHANNELS 32
#define KS1_MAX_DEVICES 64 // 最大支持的KS1设备数量
#define KS1_MAX_PATH_LEN 512 // 设备路径最大长度
#define KS1_MAX_STRING_LEN 256 // 一般字符串最大长度
#define KS1_MAX_UUID_LEN 64 // UUID字符串最大长度
#define KS1_MAX_CHIPS 16 // 最大Chip数量
#define MAX_PROCESS_ENTRIES 100 // 最大进程数
#ifdef __linux__
#ifndef __KERNEL__
#include <stdint.h>
#include <sys/types.h>
#else
#include <linux/types.h>
#include <linux/limits.h>
#include <linux/kernel.h>
#endif
#include <linux/ioctl.h>
#define _IOW_ _IOW
#define _IOR_ _IOR
#define _IOWR_ _IOWR
#define _IO_ _IO
#define CALCULET_GENERAL_IOCTL_MAGIC 'g'
#define CALCULET_DMA_IOCTL_MAGIC 'd'
#define CALCULET_SOC_IOCTL_MAGIC 's'
#define CALCULET_NNC_IOCTL_MAGIC 'n'
#define CALCULET_PCI_EP_IOCTL_MAGIC 'p'
#define CALCULET_PROC_IOCTL_MAGIC 'r'
#else
#error "unsupported platform!"
#endif
#pragma pack(push, 1)
// 传输方向枚举
enum calculet_transfer_direction {
TRANSFER_C2H_DIRECTION = 0, //device->host
TRANSFER_H2C_DIRECTION, //host->device
TRANSFER_MAX_ENUM = INT_MAX,
};
// BAR类型枚举
enum calculet_bar_type {
CALCULET_PCIE_BAR2 = 2,
CALCULET_PCIE_BAR4 = 4,
};
// BAR64传输参数
struct calculet_bar64_transfer_params {
enum calculet_transfer_direction transfer_direction; //传输方向
enum calculet_bar_type bar_type; //访问bar类型(2或4
uint64_t bar_offset; //基于bar的地址偏移
uint64_t bar_data; //读或写bar的值
};
// BAR32传输参数
struct calculet_bar32_transfer_params {
enum calculet_transfer_direction transfer_direction; //传输方向
enum calculet_bar_type bar_type; //访问bar类型(2或4
uint64_t bar_offset; //基于bar的地址偏移
uint32_t bar_data; //读或写bar的值
};
// DMA传输通道参数
struct calculet_dma_transfer_channels_params {
enum calculet_transfer_direction dma_transfer_direction; //传输方向
int dma_channel_id; //dma通道index0~7
size_t dma_channel_size; //dma传输一包数据大小(字节)
uint8_t *host_address; //应用层读或写buffer虚拟地址
uint64_t device_address; //要访问device的绝对地址
};
// 进程信息结构体
struct calculet_process_info_user {
int32_t pid; // 进程ID
int32_t ppid; // 父进程ID
uint32_t uid; // 用户ID
char comm[16]; // 进程名称(TASK_COMM_LEN
uint8_t is_active; // 是否活跃
uint8_t reserved[3]; // 对齐保留字段
};
// 进程信息列表结构体
struct calculet_process_info_list_user {
int32_t count; // 进程数量
int32_t board_index; // 板卡索引
struct calculet_process_info_user processes[MAX_PROCESS_ENTRIES];
};
struct calculet_ioc_info {
// 设备基本识别信息
char product_name[KS1_MAX_STRING_LEN]; // 产品名称
char serial_number[KS1_MAX_STRING_LEN]; // 序列号
char product_number[KS1_MAX_STRING_LEN]; // PN号
char calculet_uuid[KS1_MAX_UUID_LEN]; // calculet唯一标识符
// 设备硬件信息
uint16_t device_id; // 设备ID
uint16_t vendor_id; // 厂商ID
uint16_t subsystem_device; // 子系统ID
uint16_t subsystem_vendor; // 子系统厂商ID
// PCI信息
struct {
uint8_t domain; // PCI域
uint8_t bus; // PCI总线
uint8_t device; // PCI设备
uint8_t function; // PCI功能
uint8_t link_speed; // PCI速率
uint8_t link_width; // PCI位宽
} pci_info;
// 版本信息
struct {
uint32_t major; // 驱动主版本号
uint32_t minor; // 驱动次版本号
char string[KS1_MAX_STRING_LEN]; // 版本字符串
} driver_version;
char firmware_version[12]; // 固件版本
// 硬件规格(固定参数)
uint64_t total_memory; // 总显存大小 (0x004C, 8字节)
double memory_bandwidth; // 显存带宽 (0x0054)
uint32_t ddr_frequency; // DDR频率 (0x005C)
uint32_t max_power_limit; // KS1 TDP上限 (0x0060)
uint32_t temp_limit_ks1; // KS1温度上限 (0x0064)
};
struct calculet_msi_req {
uint32_t msi_req; // 自定义msi 请求号(0~15
};
struct calculet_sn_info {
uint32_t chip_id;
char sn[KS1_MAX_STRING_LEN];
};
#pragma pack(pop)
// 通用ioctl代码
enum calculet_general_ioctl_code {
CALCULET_BOARD_INFO_CODE,
CALCULET_BAR64_TRANSFER_CODE,
CALCULET_BAR32_TRANSFER_CODE,
CALCULET_GET_SN_CODE,
CALCULET_GENERAL_IOCTL_MAX_NR,
};
// DMA ioctl代码
enum calculet_dma_ioctl_code {
CALCULET_BLOCK_DMA_TRANSFER_CODE,
CALCULET_DMA_INTERRUPT_POLL_CODE,
CALCULET_EP_MSI_REQ_CODE,
CALCULET_SOFT_RESET_CODE,
CALCULET_DMA_IOCTL_MAX_NR,
};
// 进程信息ioctl代码
enum calculet_proc_ioctl_code {
CALCULET_GET_PROCESS_LIST_CODE,
CALCULET_PROC_IOCTL_MAX_NR,
};
// ioctl定义
// 获取calculet_ioc_info结构体所有参数
#define CALCULET_BOARD_INFO _IOWR_(CALCULET_GENERAL_IOCTL_MAGIC, CALCULET_BOARD_INFO_CODE, struct calculet_ioc_info)
// 64bit访问bar
#define CALCULET_BAR64_TRANSFER _IOWR_(CALCULET_GENERAL_IOCTL_MAGIC, CALCULET_BAR64_TRANSFER_CODE, struct calculet_bar64_transfer_params)
// 32bit访问bar
#define CALCULET_BAR32_TRANSFER _IOWR_(CALCULET_GENERAL_IOCTL_MAGIC, CALCULET_BAR32_TRANSFER_CODE, struct calculet_bar32_transfer_params)
// 获取SN信息
#define CALCULET_GET_SN _IOWR_(CALCULET_GENERAL_IOCTL_MAGIC, CALCULET_GET_SN_CODE, struct calculet_sn_info)
// dma传输
#define CALCULET_BLOCK_DMA_TRANSFER _IOWR_(CALCULET_DMA_IOCTL_MAGIC, CALCULET_BLOCK_DMA_TRANSFER_CODE, struct calculet_dma_transfer_channels_params)
// 轮询中断
#define CALCULET_INTERRUPT_POLL _IO_(CALCULET_DMA_IOCTL_MAGIC, CALCULET_DMA_INTERRUPT_POLL_CODE)
// device主动发起MSI中断(可用于jobq回馈)
#define CALCULET_EP_MSI_REQ _IOW_(CALCULET_PCI_EP_IOCTL_MAGIC, CALCULET_EP_MSI_REQ_CODE, struct calculet_msi_req)
// softreset
#define CALCULET_SOFT_RESET _IO_(CALCULET_PCI_EP_IOCTL_MAGIC, CALCULET_SOFT_RESET_CODE)
// 进程信息
#define CALCULET_GET_PROCESS_LIST _IOWR_(CALCULET_PROC_IOCTL_MAGIC, CALCULET_GET_PROCESS_LIST_CODE, struct calculet_process_info_list_user)
#endif /* _CALCULET_IOCTL_H_ */
@@ -0,0 +1,512 @@
// SPDX-License-Identifier: GPL-2.0
/**
* Copyright (c) 2024-2025 CALCULET Technologies Ltd. All rights reserved.
*/
#include "proc.h"
#include "board_mgr.h"
#include "utils.h"
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/pid.h>
#include <linux/sched.h>
#include <linux/cred.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
#include <linux/sched/signal.h>
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 17, 0)
#define GET_PDE_DATA(inode) pde_data(inode)
#elif defined(RHEL_MAJOR) && RHEL_MAJOR >= 9
#define GET_PDE_DATA(inode) pde_data(inode)
#else
#define GET_PDE_DATA(inode) PDE_DATA(inode)
#endif
static struct calculet_proc_mgr g_proc_mgr = {
.root_dir = NULL,
.summary_entry = NULL,
.processes_entry = NULL,
.initialized = false,
};
// Proc文件操作结构体
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,6,0)
static const struct proc_ops calculet_proc_summary_ops = {
.proc_open = calculet_proc_summary_open,
.proc_read = seq_read,
.proc_lseek = seq_lseek,
.proc_release = single_release,
};
static const struct proc_ops calculet_proc_device_ops = {
.proc_open = calculet_proc_device_open,
.proc_read = seq_read,
.proc_lseek = seq_lseek,
.proc_release = single_release,
};
static const struct proc_ops calculet_proc_processes_ops = {
.proc_open = calculet_proc_processes_open,
.proc_read = seq_read,
.proc_lseek = seq_lseek,
.proc_release = single_release,
};
#else
static const struct file_operations calculet_proc_summary_ops = {
.open = calculet_proc_summary_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static const struct file_operations calculet_proc_device_ops = {
.open = calculet_proc_device_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static const struct file_operations calculet_proc_processes_ops = {
.open = calculet_proc_processes_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
#endif
int calculet_proc_init(void)
{
if (g_proc_mgr.initialized) {
return 0;
}
// 创建根目录
g_proc_mgr.root_dir = proc_mkdir(CALCULET_PROC_DIR_NAME, NULL);
if (!g_proc_mgr.root_dir) {
pr_err("calculet: Failed to create proc directory %s\n", CALCULET_PROC_DIR_NAME);
return -ENOMEM;
}
// 创建摘要文件
g_proc_mgr.summary_entry = proc_create("summary", 0444, g_proc_mgr.root_dir,
&calculet_proc_summary_ops);
if (!g_proc_mgr.summary_entry) {
pr_err("calculet: Failed to create proc summary file\n");
goto cleanup_root;
}
// 创建全局进程监测文件
g_proc_mgr.processes_entry = proc_create("processes", 0444, g_proc_mgr.root_dir,
&calculet_proc_processes_ops);
if (!g_proc_mgr.processes_entry) {
pr_err("calculet: Failed to create proc processes file\n");
goto cleanup_summary;
}
g_proc_mgr.initialized = true;
pr_info("calculet: Proc filesystem initialized\n");
return 0;
cleanup_summary:
proc_remove(g_proc_mgr.summary_entry);
g_proc_mgr.summary_entry = NULL;
cleanup_root:
proc_remove(g_proc_mgr.root_dir);
g_proc_mgr.root_dir = NULL;
return -ENOMEM;
}
void calculet_proc_cleanup(void)
{
if (!g_proc_mgr.initialized) {
return;
}
if (g_proc_mgr.processes_entry) {
proc_remove(g_proc_mgr.processes_entry);
g_proc_mgr.processes_entry = NULL;
}
if (g_proc_mgr.summary_entry) {
proc_remove(g_proc_mgr.summary_entry);
g_proc_mgr.summary_entry = NULL;
}
if (g_proc_mgr.root_dir) {
proc_remove(g_proc_mgr.root_dir);
g_proc_mgr.root_dir = NULL;
}
g_proc_mgr.initialized = false;
pr_info("calculet: Proc filesystem cleaned up\n");
}
int calculet_proc_add_device(struct calculet_pcie_board *board)
{
if (!g_proc_mgr.initialized || !board) {
return -EINVAL;
}
board->proc_entry = proc_create_data(board->proc_name, 0444, g_proc_mgr.root_dir,
&calculet_proc_device_ops, board);
if (!board->proc_entry) {
pr_err("calculet: Failed to create proc entry for device %d\n", board->board_index);
return -ENOMEM;
}
return 0;
}
void calculet_proc_remove_device(struct calculet_pcie_board *board)
{
if (board && board->proc_entry) {
proc_remove(board->proc_entry);
board->proc_entry = NULL;
}
}
// // 获取进程完整执行路径的函数
// static char *get_process_exe_path(struct task_struct *task, char *buffer, int buflen)
// {
// struct file *exe_file;
// char *path = NULL;
// if (!task || !task->mm) {
// return NULL;
// }
// // 获取可执行文件
// exe_file = get_task_exe_file(task);
// if (!exe_file) {
// return NULL;
// }
// // 获取文件路径
// path = d_path(&exe_file->f_path, buffer, buflen);
// fput(exe_file);
// return IS_ERR(path) ? NULL : path;
// }
// 进程监测相关函数
int calculet_proc_add_process(struct calculet_pcie_board *board, struct task_struct *task)
{
struct calculet_process_info *proc_info;
struct calculet_process_info *existing;
bool found = false;
char path_buffer[256];
char *exe_path;
if (!board || !task) {
return -EINVAL;
}
mutex_lock(&board->process_list_mutex);
// 检查是否已经存在该进程
list_for_each_entry(existing, &board->process_list, list) {
if (existing->pid == task->pid) {
if (!existing->is_active) {
// 重新激活已关闭的进程
existing->is_active = true;
found = true;
break;
} else {
found = true;
break;
}
}
}
if (!found) {
proc_info = kzalloc(sizeof(*proc_info), GFP_KERNEL);
if (!proc_info) {
mutex_unlock(&board->process_list_mutex);
return -ENOMEM;
}
proc_info->pid = task->pid;
proc_info->ppid = task->real_parent ? task->real_parent->pid : 0;
proc_info->uid = from_kuid(&init_user_ns, task_uid(task));
strncpy(proc_info->comm, task->comm, TASK_COMM_LEN - 1);
proc_info->comm[TASK_COMM_LEN - 1] = '\0';
proc_info->is_active = true;
list_add_tail(&proc_info->list, &board->process_list);
atomic_inc(&board->process_count);
// // 获取完整路径(如果需要的话)
// exe_path = get_process_exe_path(task, path_buffer, sizeof(path_buffer));
// if (exe_path) {
// // 可以添加一个新字段存储完整路径
// // strncpy(proc_info->exe_path, exe_path, MAX_PATH_LEN - 1);
// calculet_board_log(CALCULET_LOG_DEBUG, board,
// "Process full path: %s", exe_path);
// }
calculet_board_log(CALCULET_LOG_DEBUG, board,
"Process %s (PID: %d) started using device %d",
proc_info->comm, proc_info->pid, board->board_index);
}
mutex_unlock(&board->process_list_mutex);
return 0;
}
void calculet_proc_remove_process(struct calculet_pcie_board *board, pid_t pid)
{
struct calculet_process_info *proc_info;
if (!board) {
return;
}
mutex_lock(&board->process_list_mutex);
list_for_each_entry(proc_info, &board->process_list, list) {
if (proc_info->pid == pid && proc_info->is_active) {
proc_info->is_active = false;
calculet_board_log(CALCULET_LOG_DEBUG, board,
"Process %s (PID: %d) stopped using device %d",
proc_info->comm, proc_info->pid, board->board_index);
break;
}
}
mutex_unlock(&board->process_list_mutex);
}
void calculet_proc_update_process_access(struct calculet_pcie_board *board, pid_t pid)
{
struct calculet_process_info *proc_info;
if (!board) {
return;
}
mutex_lock(&board->process_list_mutex);
list_for_each_entry(proc_info, &board->process_list, list) {
if (proc_info->pid == pid && proc_info->is_active) {
break;
}
}
mutex_unlock(&board->process_list_mutex);
}
void calculet_proc_cleanup_processes(struct calculet_pcie_board *board)
{
struct calculet_process_info *proc_info, *tmp;
if (!board) {
return;
}
mutex_lock(&board->process_list_mutex);
list_for_each_entry_safe(proc_info, tmp, &board->process_list, list) {
list_del(&proc_info->list);
kfree(proc_info);
atomic_dec(&board->process_count);
}
mutex_unlock(&board->process_list_mutex);
}
// 简化版摘要显示函数 - 只显示核心统计信息
int calculet_proc_summary_show(struct seq_file *m, void *v)
{
struct calculet_pcie_board *boards[MAX_CALCULET_BOARDS];
int board_count, i;
int total_active_processes = 0;
board_count = calculet_board_mgr_get_all_boards(boards, MAX_CALCULET_BOARDS);
seq_printf(m, "CALCULET PCIe Driver Status\n");
seq_printf(m, "===========================\n");
seq_printf(m, "Driver Version: %s\n", CALCULET_DRV_VER);
seq_printf(m, "Active Boards: %d\n", board_count);
seq_printf(m, "\n");
if (board_count == 0) {
seq_printf(m, "No active boards found.\n");
return 0;
}
seq_printf(m, "Board Summary:\n");
seq_printf(m, "Board State Bus:Dev.Func Active Procs\n");
seq_printf(m, "----------------------------------------\n");
for (i = 0; i < board_count; i++) {
struct calculet_pcie_board *board = boards[i];
enum calculet_board_state state = calculet_board_get_state(board);
const char *state_str;
int active_procs = 0;
struct calculet_process_info *proc_info;
switch (state) {
case BOARD_STATE_ACTIVE: state_str = "ACTIVE"; break;
case BOARD_STATE_REMOVING: state_str = "REMOVE"; break;
default: state_str = "UNKNOWN"; break;
}
// 统计活跃进程数
mutex_lock(&board->process_list_mutex);
list_for_each_entry(proc_info, &board->process_list, list) {
if (proc_info->is_active) {
active_procs++;
}
}
mutex_unlock(&board->process_list_mutex);
seq_printf(m, "%-5d %-7s %02x:%02x.%x %-11d\n",
board->board_index, state_str,
board->pDev->bus->number, PCI_SLOT(board->pDev->devfn), PCI_FUNC(board->pDev->devfn),
active_procs);
total_active_processes += active_procs;
// 释放引用
calculet_board_mgr_put_board(board);
}
seq_printf(m, "----------------------------------------\n");
seq_printf(m, "Total Active Processes: %d\n", total_active_processes);
return 0;
}
int calculet_proc_summary_open(struct inode *inode, struct file *file)
{
return single_open(file, calculet_proc_summary_show, NULL);
}
// 简化版设备详细信息显示函数
int calculet_proc_device_show(struct seq_file *m, void *v)
{
struct calculet_pcie_board *board = (struct calculet_pcie_board *)m->private;
struct calculet_process_info *proc_info;
int active_processes = 0;
enum calculet_board_state state;
if (!board) {
seq_printf(m, "Error: Invalid board data\n");
return 0;
}
state = calculet_board_get_state(board);
seq_printf(m, "CALCULET Board %d Information\n", board->board_index);
seq_printf(m, "=============================\n\n");
// 板卡基本状态
seq_printf(m, "Board Status:\n");
seq_printf(m, " State: %s\n",
state == BOARD_STATE_ACTIVE ? "ACTIVE" :
state == BOARD_STATE_REMOVING ? "REMOVING" : "UNKNOWN");
seq_printf(m, " Operational: %s\n", calculet_board_is_operational(board) ? "YES" : "NO");
seq_printf(m, "\n");
// PCIe基本信息
seq_printf(m, "PCIe Information:\n");
seq_printf(m, " Device ID: %04x:%04x\n", board->pDev->vendor, board->pDev->device);
seq_printf(m, " Bus:Dev.Func: %02x:%02x.%x\n",
board->pDev->bus->number, PCI_SLOT(board->pDev->devfn), PCI_FUNC(board->pDev->devfn));
seq_printf(m, "\n");
// 进程信息 - 只显示活跃进程
mutex_lock(&board->process_list_mutex);
list_for_each_entry(proc_info, &board->process_list, list) {
if (proc_info->is_active) {
active_processes++;
}
}
seq_printf(m, "Active Process Information:\n");
seq_printf(m, " Active Processes: %d\n", active_processes);
seq_printf(m, "\n");
if (active_processes > 0) {
seq_printf(m, "Active Process Details:\n");
seq_printf(m, "PID PPID UID COMM\n");
seq_printf(m, "------------------------------------\n");
list_for_each_entry(proc_info, &board->process_list, list) {
if (proc_info->is_active) {
seq_printf(m, "%-7d %-7d %-7u %s\n",
proc_info->pid, proc_info->ppid, proc_info->uid,
proc_info->comm);
}
}
}
mutex_unlock(&board->process_list_mutex);
return 0;
}
int calculet_proc_device_open(struct inode *inode, struct file *file)
{
return single_open(file, calculet_proc_device_show, GET_PDE_DATA(inode));
}
// 简化版全局进程监测显示函数
int calculet_proc_processes_show(struct seq_file *m, void *v)
{
struct calculet_pcie_board *boards[MAX_CALCULET_BOARDS];
int board_count, i;
struct calculet_process_info *proc_info;
int total_active = 0;
board_count = calculet_board_mgr_get_all_boards(boards, MAX_CALCULET_BOARDS);
seq_printf(m, "CALCULET Active Process Monitor\n");
seq_printf(m, "===============================\n\n");
if (board_count == 0) {
seq_printf(m, "No active boards found.\n");
return 0;
}
seq_printf(m, "BOARD PID PPID UID COMM\n");
seq_printf(m, "-----------------------------------\n");
for (i = 0; i < board_count; i++) {
struct calculet_pcie_board *board = boards[i];
mutex_lock(&board->process_list_mutex);
list_for_each_entry(proc_info, &board->process_list, list) {
if (proc_info->is_active) { // 只显示活跃进程
seq_printf(m, "%-5d %-7d %-7d %-7u %s\n",
board->board_index,
proc_info->pid, proc_info->ppid, proc_info->uid,
proc_info->comm);
total_active++;
}
}
mutex_unlock(&board->process_list_mutex);
// 释放引用
calculet_board_mgr_put_board(board);
}
seq_printf(m, "---------------------------------------------------\n");
seq_printf(m, "Total active processes: %d\n", total_active);
return 0;
}
int calculet_proc_processes_open(struct inode *inode, struct file *file)
{
return single_open(file, calculet_proc_processes_show, NULL);
}
@@ -0,0 +1,63 @@
// SPDX-License-Identifier: GPL-2.0
/**
* Copyright (c) 2024-2025 CALCULET Technologies Ltd. All rights reserved.
*/
#ifndef _CALCULET_PROC_H_
#define _CALCULET_PROC_H_
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/list.h>
#include <linux/sched.h>
#include <linux/time.h>
#include <linux/atomic.h>
#define CALCULET_PROC_DIR_NAME "calculet_monitor"
#define MAX_PROCESS_ENTRIES 100
// 前向声明
struct calculet_pcie_board;
// 简化的内核进程信息结构体
struct calculet_process_info {
struct list_head list; // 链表节点
pid_t pid; // 进程ID
pid_t ppid; // 父进程ID
uid_t uid; // 用户ID
char comm[TASK_COMM_LEN]; // 进程名称
bool is_active; // 是否仍然活跃
};
// Proc文件系统管理结构体
struct calculet_proc_mgr {
struct proc_dir_entry *root_dir;
struct proc_dir_entry *summary_entry;
struct proc_dir_entry *processes_entry;
bool initialized;
};
// 函数声明
int calculet_proc_init(void);
void calculet_proc_cleanup(void);
int calculet_proc_add_device(struct calculet_pcie_board *board);
void calculet_proc_remove_device(struct calculet_pcie_board *board);
// 进程监测相关函数
int calculet_proc_add_process(struct calculet_pcie_board *board, struct task_struct *task);
void calculet_proc_remove_process(struct calculet_pcie_board *board, pid_t pid);
void calculet_proc_update_process_access(struct calculet_pcie_board *board, pid_t pid);
void calculet_proc_cleanup_processes(struct calculet_pcie_board *board);
// Proc文件显示函数
int calculet_proc_summary_show(struct seq_file *m, void *v);
int calculet_proc_summary_open(struct inode *inode, struct file *file);
int calculet_proc_device_show(struct seq_file *m, void *v);
int calculet_proc_device_open(struct inode *inode, struct file *file);
int calculet_proc_processes_show(struct seq_file *m, void *v);
int calculet_proc_processes_open(struct inode *inode, struct file *file);
#endif /* _CALCULET_PROC_H_ */
@@ -0,0 +1,420 @@
// SPDX-License-Identifier: GPL-2.0
/**
* Copyright (c) 2024-2025 CALCULET Technologies Ltd. All rights reserved.
*/
#include "utils.h"
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/random.h>
// 全局日志级别控制(可通过模块参数调整)
#ifndef CALCULET_LOG_LEVEL
#define CALCULET_LOG_LEVEL CALCULET_LOG_INFO
#endif
int calculet_log_level = CALCULET_LOG_LEVEL;
// 模块参数,允许运行时调整日志级别
module_param(calculet_log_level, int, 0644);
MODULE_PARM_DESC(calculet_log_level, "Debug level (0=EMERG, 1=ALERT, 2=CRIT, 3=ERR, 4=WARN, 5=NOTICE, 6=INFO, 7=DEBUG)");
// 全局统计信息
static struct calculet_stats_counter g_global_stats;
static bool g_stats_initialized = false;
static DEFINE_SPINLOCK(g_stats_lock);
/**
* 初始化全局统计
*/
void calculet_global_stats_init(void)
{
spin_lock(&g_stats_lock);
if (!g_stats_initialized) {
calculet_stats_init(&g_global_stats);
g_stats_initialized = true;
calculet_log(CALCULET_LOG_DEBUG, "Global statistics initialized");
}
spin_unlock(&g_stats_lock);
}
/**
* 更新全局统计
*/
void calculet_global_stats_update(s64 value)
{
if (g_stats_initialized) {
calculet_stats_update(&g_global_stats, value);
}
}
/**
* 获取全局统计信息
*/
void calculet_global_stats_get(s64 *count, s64 *total, s64 *min, s64 *max, s64 *avg)
{
if (!g_stats_initialized) {
if (count) *count = 0;
if (total) *total = 0;
if (min) *min = 0;
if (max) *max = 0;
if (avg) *avg = 0;
return;
}
if (count) *count = atomic64_read(&g_global_stats.count);
if (total) *total = atomic64_read(&g_global_stats.total);
if (min) *min = atomic64_read(&g_global_stats.min);
if (max) *max = atomic64_read(&g_global_stats.max);
if (avg) *avg = calculet_stats_avg(&g_global_stats);
}
/**
* 清理全局统计
*/
void calculet_global_stats_cleanup(void)
{
spin_lock(&g_stats_lock);
g_stats_initialized = false;
spin_unlock(&g_stats_lock);
calculet_log(CALCULET_LOG_DEBUG, "Global statistics cleaned up");
}
/**
* 内存分配跟踪器(仅在调试模式下)
*/
#ifdef CALCULET_DEBUG_MODE
struct calculet_mem_info {
void *ptr;
size_t size;
const char *file;
int line;
ktime_t alloc_time;
struct list_head list;
};
static LIST_HEAD(g_mem_list);
static DEFINE_SPINLOCK(g_mem_lock);
static atomic_t g_mem_count = ATOMIC_INIT(0);
static atomic64_t g_mem_total = ATOMIC64_INIT(0);
void *calculet_kmalloc_tracked(size_t size, gfp_t flags, const char *file, int line)
{
struct calculet_mem_info *info;
void *ptr;
ptr = kmalloc(size, flags);
if (!ptr) {
return NULL;
}
info = kmalloc(sizeof(*info), GFP_ATOMIC);
if (!info) {
kfree(ptr);
return NULL;
}
info->ptr = ptr;
info->size = size;
info->file = file;
info->line = line;
info->alloc_time = ktime_get();
spin_lock(&g_mem_lock);
list_add_tail(&info->list, &g_mem_list);
spin_unlock(&g_mem_lock);
atomic_inc(&g_mem_count);
atomic64_add(size, &g_mem_total);
calculet_log(CALCULET_LOG_DEBUG, "ALLOC %p (%zu bytes) at %s:%d, total=%d allocations, %lld bytes",
ptr, size, file, line, atomic_read(&g_mem_count), atomic64_read(&g_mem_total));
return ptr;
}
void calculet_kfree_tracked(void *ptr, const char *file, int line)
{
struct calculet_mem_info *info, *tmp;
bool found = false;
if (!ptr) {
return;
}
spin_lock(&g_mem_lock);
list_for_each_entry_safe(info, tmp, &g_mem_list, list) {
if (info->ptr == ptr) {
list_del(&info->list);
found = true;
break;
}
}
spin_unlock(&g_mem_lock);
if (found) {
atomic_dec(&g_mem_count);
atomic64_sub(info->size, &g_mem_total);
calculet_log(CALCULET_LOG_DEBUG, "FREE %p (%zu bytes) at %s:%d, allocated at %s:%d, lifetime=%lld us",
ptr, info->size, file, line, info->file, info->line,
calculet_ktime_to_us(ktime_sub(ktime_get(), info->alloc_time)));
kfree(info);
} else {
calculet_log(CALCULET_LOG_WARN, "FREE unknown pointer %p at %s:%d", ptr, file, line);
}
kfree(ptr);
}
void calculet_mem_dump_leaks(void)
{
struct calculet_mem_info *info;
int leak_count = 0;
calculet_log(CALCULET_LOG_DEBUG, "Memory leak check - %d allocations, %lld bytes total",
atomic_read(&g_mem_count), atomic64_read(&g_mem_total));
spin_lock(&g_mem_lock);
list_for_each_entry(info, &g_mem_list, list) {
calculet_log(CALCULET_LOG_WARN, "LEAK %p (%zu bytes) allocated at %s:%d, age=%lld us",
info->ptr, info->size, info->file, info->line,
calculet_ktime_to_us(ktime_sub(ktime_get(), info->alloc_time)));
leak_count++;
}
spin_unlock(&g_mem_lock);
if (leak_count == 0) {
calculet_log(CALCULET_LOG_DEBUG, "No memory leaks detected");
} else {
calculet_log(CALCULET_LOG_ERROR, "%d memory leaks detected!", leak_count);
}
}
void calculet_mem_cleanup_tracking(void)
{
struct calculet_mem_info *info, *tmp;
spin_lock(&g_mem_lock);
list_for_each_entry_safe(info, tmp, &g_mem_list, list) {
list_del(&info->list);
kfree(info);
}
spin_unlock(&g_mem_lock);
atomic_set(&g_mem_count, 0);
atomic64_set(&g_mem_total, 0);
}
#endif /* CALCULET_DEBUG_MODE */
/**
* 随机延迟函数(用于测试)
*/
void calculet_random_delay_us(unsigned int min_us, unsigned int max_us)
{
unsigned int delay_us;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)
delay_us = min_us + (get_random_u32() % (max_us - min_us + 1));
#else
delay_us = min_us + (get_random_int() % (max_us - min_us + 1));
#endif
calculet_udelay_range(delay_us, delay_us + 10);
}
/**
* 字符串工具函数
*/
char *calculet_strdup(const char *s, gfp_t flags)
{
char *dup;
size_t len;
if (!s) {
return NULL;
}
len = strlen(s) + 1;
dup = kmalloc(len, flags);
if (dup) {
memcpy(dup, s, len);
}
return dup;
}
int calculet_snprintf_safe(char *buf, size_t size, const char *fmt, ...)
{
va_list args;
int ret;
if (!buf || size == 0) {
return -EINVAL;
}
va_start(args, fmt);
ret = vsnprintf(buf, size, fmt, args);
va_end(args);
// 确保字符串以null结尾
buf[size - 1] = '\0';
return ret;
}
/**
* 位操作工具函数
*/
unsigned int calculet_ffs(unsigned long word)
{
return __ffs(word);
}
unsigned int calculet_fls(unsigned long word)
{
return fls(word);
}
unsigned int calculet_popcount(unsigned long word)
{
return hweight_long(word);
}
/**
* 时间工具函数
*/
u64 calculet_get_cycles(void)
{
return get_cycles();
}
ktime_t calculet_get_monotonic_time(void)
{
return ktime_get();
}
ktime_t calculet_get_real_time(void)
{
return ktime_get_real();
}
/**
* CRC32计算(简单版本)
*/
static const u32 crc32_table[256] = {
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};
u32 calculet_crc32(u32 crc, const void *buf, size_t len)
{
const u8 *data = (const u8 *)buf;
crc = ~crc;
while (len--) {
crc = crc32_table[(crc ^ *data++) & 0xff] ^ (crc >> 8);
}
return ~crc;
}
/**
* 模块清理函数
*/
void calculet_utils_cleanup(void)
{
#ifdef CALCULET_DEBUG_MODE
calculet_mem_dump_leaks();
calculet_mem_cleanup_tracking();
#endif
calculet_global_stats_cleanup();
}
/**
* 模块初始化函数
*/
int calculet_utils_init(void)
{
calculet_global_stats_init();
calculet_log(CALCULET_LOG_DEBUG, "Utils module initialized (log_level=%d, debug_mode=%s)",
calculet_log_level,
#ifdef CALCULET_DEBUG_MODE
"enabled"
#else
"disabled"
#endif
);
return 0;
}
// 导出符号供其他模块使用
EXPORT_SYMBOL(calculet_log_level);
EXPORT_SYMBOL(calculet_global_stats_init);
EXPORT_SYMBOL(calculet_global_stats_update);
EXPORT_SYMBOL(calculet_global_stats_get);
EXPORT_SYMBOL(calculet_global_stats_cleanup);
#ifdef CALCULET_DEBUG_MODE
EXPORT_SYMBOL(calculet_kmalloc_tracked);
EXPORT_SYMBOL(calculet_kfree_tracked);
EXPORT_SYMBOL(calculet_mem_dump_leaks);
EXPORT_SYMBOL(calculet_mem_cleanup_tracking);
#endif
EXPORT_SYMBOL(calculet_random_delay_us);
EXPORT_SYMBOL(calculet_strdup);
EXPORT_SYMBOL(calculet_snprintf_safe);
EXPORT_SYMBOL(calculet_ffs);
EXPORT_SYMBOL(calculet_fls);
EXPORT_SYMBOL(calculet_popcount);
EXPORT_SYMBOL(calculet_get_cycles);
EXPORT_SYMBOL(calculet_get_monotonic_time);
EXPORT_SYMBOL(calculet_get_real_time);
EXPORT_SYMBOL(calculet_crc32);
EXPORT_SYMBOL(calculet_utils_init);
EXPORT_SYMBOL(calculet_utils_cleanup);
@@ -0,0 +1,335 @@
// 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_ */