data: publish complete Calculet NPU research archive
This commit is contained in:
+13116
File diff suppressed because it is too large
Load Diff
+59
@@ -0,0 +1,59 @@
|
||||
DKMS make.log for cal-pcie-0.9.0 for kernel 6.17.0-35-generic (x86_64)
|
||||
2026年 06月 03日 星期三 17:44:27 CST
|
||||
make: 进入目录“/usr/src/linux-headers-6.17.0-35-generic”
|
||||
make[1]: 进入目录“/var/lib/dkms/cal-pcie/0.9.0/build”
|
||||
warning: the compiler differs from the one used to build the kernel
|
||||
The kernel was built by: x86_64-linux-gnu-gcc-13 (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0
|
||||
You are using: gcc-13 (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0
|
||||
CC [M] calculet_pci_core.o
|
||||
CC [M] board_mgr.o
|
||||
CC [M] proc.o
|
||||
CC [M] dma.o
|
||||
CC [M] fops.o
|
||||
CC [M] utils.o
|
||||
utils.c:231:7: warning: no previous prototype for ‘calculet_strdup’ [-Wmissing-prototypes]
|
||||
231 | char *calculet_strdup(const char *s, gfp_t flags)
|
||||
| ^~~~~~~~~~~~~~~
|
||||
utils.c:249:5: warning: no previous prototype for ‘calculet_snprintf_safe’ [-Wmissing-prototypes]
|
||||
249 | int calculet_snprintf_safe(char *buf, size_t size, const char *fmt, ...)
|
||||
| ^~~~~~~~~~~~~~~~~~~~~~
|
||||
utils.c:271:14: warning: no previous prototype for ‘calculet_ffs’ [-Wmissing-prototypes]
|
||||
271 | unsigned int calculet_ffs(unsigned long word)
|
||||
| ^~~~~~~~~~~~
|
||||
utils.c:276:14: warning: no previous prototype for ‘calculet_fls’ [-Wmissing-prototypes]
|
||||
276 | unsigned int calculet_fls(unsigned long word)
|
||||
| ^~~~~~~~~~~~
|
||||
utils.c:281:14: warning: no previous prototype for ‘calculet_popcount’ [-Wmissing-prototypes]
|
||||
281 | unsigned int calculet_popcount(unsigned long word)
|
||||
| ^~~~~~~~~~~~~~~~~
|
||||
utils.c:289:5: warning: no previous prototype for ‘calculet_get_cycles’ [-Wmissing-prototypes]
|
||||
289 | u64 calculet_get_cycles(void)
|
||||
| ^~~~~~~~~~~~~~~~~~~
|
||||
utils.c:294:9: warning: no previous prototype for ‘calculet_get_monotonic_time’ [-Wmissing-prototypes]
|
||||
294 | ktime_t calculet_get_monotonic_time(void)
|
||||
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
utils.c:299:9: warning: no previous prototype for ‘calculet_get_real_time’ [-Wmissing-prototypes]
|
||||
299 | ktime_t calculet_get_real_time(void)
|
||||
| ^~~~~~~~~~~~~~~~~~~~~~
|
||||
utils.c:353:5: warning: no previous prototype for ‘calculet_crc32’ [-Wmissing-prototypes]
|
||||
353 | u32 calculet_crc32(u32 crc, const void *buf, size_t len)
|
||||
| ^~~~~~~~~~~~~~
|
||||
proc.c: In function ‘calculet_proc_add_process’:
|
||||
proc.c:202:11: warning: unused variable ‘exe_path’ [-Wunused-variable]
|
||||
202 | char *exe_path;
|
||||
| ^~~~~~~~
|
||||
proc.c:201:10: warning: unused variable ‘path_buffer’ [-Wunused-variable]
|
||||
201 | char path_buffer[256];
|
||||
| ^~~~~~~~~~~
|
||||
fops.c:48:6: warning: no previous prototype for ‘calculet_generate_simple_uuid’ [-Wmissing-prototypes]
|
||||
48 | void calculet_generate_simple_uuid(const char *serial_number, char *uuid_str)
|
||||
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
LD [M] calculet_pci.o
|
||||
MODPOST Module.symvers
|
||||
CC [M] calculet_pci.mod.o
|
||||
CC [M] .module-common.o
|
||||
LD [M] calculet_pci.ko
|
||||
BTF [M] calculet_pci.ko
|
||||
Skipping BTF generation for calculet_pci.ko due to unavailability of vmlinux
|
||||
make[1]: 离开目录“/var/lib/dkms/cal-pcie/0.9.0/build”
|
||||
make: 离开目录“/usr/src/linux-headers-6.17.0-35-generic”
|
||||
BIN
Binary file not shown.
+13329
File diff suppressed because it is too large
Load Diff
+62
@@ -0,0 +1,62 @@
|
||||
DKMS make.log for cal-pcie-0.9.0 for kernel 7.0.0-28-generic (x86_64)
|
||||
2026年 07月 18日 星期六 06:22:39 CST
|
||||
make: 进入目录“/usr/src/linux-headers-7.0.0-28-generic”
|
||||
make[1]: 进入目录“/var/lib/dkms/cal-pcie/0.9.0/build”
|
||||
warning: the compiler differs from the one used to build the kernel
|
||||
The kernel was built by: x86_64-linux-gnu-gcc-13 (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0
|
||||
You are using: gcc-13 (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0
|
||||
warning: pahole version differs from the one used to build the kernel
|
||||
The kernel was built with: 125
|
||||
You are using: 0
|
||||
CC [M] calculet_pci_core.o
|
||||
CC [M] board_mgr.o
|
||||
CC [M] proc.o
|
||||
CC [M] dma.o
|
||||
CC [M] fops.o
|
||||
CC [M] utils.o
|
||||
utils.c:231:7: warning: no previous prototype for ‘calculet_strdup’ [-Wmissing-prototypes]
|
||||
231 | char *calculet_strdup(const char *s, gfp_t flags)
|
||||
| ^~~~~~~~~~~~~~~
|
||||
utils.c:249:5: warning: no previous prototype for ‘calculet_snprintf_safe’ [-Wmissing-prototypes]
|
||||
249 | int calculet_snprintf_safe(char *buf, size_t size, const char *fmt, ...)
|
||||
| ^~~~~~~~~~~~~~~~~~~~~~
|
||||
utils.c:271:14: warning: no previous prototype for ‘calculet_ffs’ [-Wmissing-prototypes]
|
||||
271 | unsigned int calculet_ffs(unsigned long word)
|
||||
| ^~~~~~~~~~~~
|
||||
utils.c:276:14: warning: no previous prototype for ‘calculet_fls’ [-Wmissing-prototypes]
|
||||
276 | unsigned int calculet_fls(unsigned long word)
|
||||
| ^~~~~~~~~~~~
|
||||
utils.c:281:14: warning: no previous prototype for ‘calculet_popcount’ [-Wmissing-prototypes]
|
||||
281 | unsigned int calculet_popcount(unsigned long word)
|
||||
| ^~~~~~~~~~~~~~~~~
|
||||
utils.c:289:5: warning: no previous prototype for ‘calculet_get_cycles’ [-Wmissing-prototypes]
|
||||
289 | u64 calculet_get_cycles(void)
|
||||
| ^~~~~~~~~~~~~~~~~~~
|
||||
utils.c:294:9: warning: no previous prototype for ‘calculet_get_monotonic_time’ [-Wmissing-prototypes]
|
||||
294 | ktime_t calculet_get_monotonic_time(void)
|
||||
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
utils.c:299:9: warning: no previous prototype for ‘calculet_get_real_time’ [-Wmissing-prototypes]
|
||||
299 | ktime_t calculet_get_real_time(void)
|
||||
| ^~~~~~~~~~~~~~~~~~~~~~
|
||||
utils.c:353:5: warning: no previous prototype for ‘calculet_crc32’ [-Wmissing-prototypes]
|
||||
353 | u32 calculet_crc32(u32 crc, const void *buf, size_t len)
|
||||
| ^~~~~~~~~~~~~~
|
||||
fops.c:48:6: warning: no previous prototype for ‘calculet_generate_simple_uuid’ [-Wmissing-prototypes]
|
||||
48 | void calculet_generate_simple_uuid(const char *serial_number, char *uuid_str)
|
||||
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
proc.c: In function ‘calculet_proc_add_process’:
|
||||
proc.c:202:11: warning: unused variable ‘exe_path’ [-Wunused-variable]
|
||||
202 | char *exe_path;
|
||||
| ^~~~~~~~
|
||||
proc.c:201:10: warning: unused variable ‘path_buffer’ [-Wunused-variable]
|
||||
201 | char path_buffer[256];
|
||||
| ^~~~~~~~~~~
|
||||
LD [M] calculet_pci.o
|
||||
MODPOST Module.symvers
|
||||
CC [M] calculet_pci.mod.o
|
||||
CC [M] .module-common.o
|
||||
LD [M] calculet_pci.ko
|
||||
BTF [M] calculet_pci.ko
|
||||
Skipping BTF generation for calculet_pci.ko due to unavailability of vmlinux
|
||||
make[1]: 离开目录“/var/lib/dkms/cal-pcie/0.9.0/build”
|
||||
make: 离开目录“/usr/src/linux-headers-7.0.0-28-generic”
|
||||
BIN
Binary file not shown.
+334
@@ -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
|
||||
+381
@@ -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;
|
||||
}
|
||||
+169
@@ -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_ */
|
||||
+195
@@ -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");
|
||||
+1108
File diff suppressed because it is too large
Load Diff
+71
@@ -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_ */
|
||||
+78
@@ -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
|
||||
+966
@@ -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));
|
||||
}
|
||||
}
|
||||
+220
@@ -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_ */
|
||||
+645
@@ -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;
|
||||
}
|
||||
+39
@@ -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_ */
|
||||
+207
@@ -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通道index(0~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_ */
|
||||
+512
@@ -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);
|
||||
}
|
||||
+63
@@ -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_ */
|
||||
+420
@@ -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);
|
||||
+335
@@ -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_ */
|
||||
Reference in New Issue
Block a user