23-从 Linux 4.0 到 7.2.5 的迁移

23.1 迁移总览

维度 4.0 7.2.5 影响
硬件描述 板级文件 + 部分设备树 设备树/ACPI 唯一主线 板级文件全部删除
编译器 GCC 4.x GCC 11+ / Clang(LLVM=1) 新警告默认开启
C 标准 GNU89/GNU11 GNU11 变量声明位置、fallthrough
模块签名 可选 常用(Secure Boot) sign-file
符号版本 CRC32 CRC32 或 DWARF(CONFIG_GENDWARFKSYMS) Rust 必需
构建 -C $KDIR M=$PWD -f $KDIR/Makefile M=$PWD(6.13+) 外部模块 Makefile
字符设备 cdev + file_operations 同,但 class_create(name) 单参数 见 23.3
中断 request_irq + tasklet threaded IRQ + 工作队列 tasklet 仅维护旧代码
定时器 init_timer() timer_setup() init_timer 已删除
块设备 请求队列 blk-mq 旧接口删除
网络 netdev_ops 雏形 NAPI + DMA + XDP + ethtool_ops 见 23.6
总线 i2c_new_device i2c_new_client_device 见 23.7
GPIO 整数 GPIO 描述符 API 见 23.8
电源 手写 clk/regulator CCF/regulator/OPP/genpd 见 23.9
调试 printk + GDB + dynamic debug/ftrace/perf/eBPF/KASAN 见第 21 章

23.2 编译与构建

23.2.1 外部模块 Makefile

bash 复制代码
# ---- 4.0 时代 ----
# KDIR ?= /lib/modules/$(shell uname -r)/build
# all:
# 	$(MAKE) -C $(KDIR) M=$(PWD) modules

# ---- 7.2.5 推荐(6.13 起支持 -f)----
obj-m += mydrv.o
KDIR ?= ~/works/study/build/linux-7.2.5-x86_64
PWD  := $(shell pwd)

all:
	$(MAKE) -f $(KDIR)/Makefile M=$(PWD) modules

clean:
	$(MAKE) -f $(KDIR)/Makefile M=$(PWD) clean

-f 写法与 -C 等价,但不切换工作目录,路径处理更直观。

23.2.2 内核编译

bash 复制代码
# 4.0 时代
# make menuconfig && make -j8

# 7.2.5 推荐(外部构建 + 配置片段)
export KSRC=~works/study/linux-7.2.5
export KBUILD=~works/study/build/linux-7.2.5-x86_64

make -C "$KSRC" O="$KBUILD" x86_64_defconfig
"$KSRC/scripts/kconfig/merge_config.sh" -O "$KBUILD" \
    "$KBUILD/.config" "$KSRC/kernel/configs/kvm_guest.config"
make -C "$KSRC" O="$KBUILD" olddefconfig
make -C "$KSRC" O="$KBUILD" -j"$(nproc)"

# Clang/LLVM 构建
make -C "$KSRC" O="$KBUILD" LLVM=1 defconfig
make -C "$KSRC" O="$KBUILD" LLVM=1 -j"$(nproc)"

自带的配置片段 (kernel/configs/):kvm_guest.config、debug.config、 hardening.config、rust.config、x86_debug.config、nopm.config。

23.2.3 编译器/语言变化

变化 说明 应对
GNU11 变量可在块中间声明 无(更好写)
-Wimplicit-fallthrough switch 穿透要显式 加 fallthrough;
-Wunused-but-set-variable 未使用变量报错 删除
-Wformat-truncation 可能截断的 snprintf 检查缓冲区
strlcpy 移除 用 strscpy 见 23.4
strncpy 不推荐 用 strscpy/strscpy_pad

23.3 字符设备与设备模型

4.0 写法 7.2.5 写法 说明
class_create(owner, name) class_create(name) 少一个参数
class_destroy(cls) class_destroy(cls) 不变
device_create(cls, parent, devt, drvdata, fmt, ...) 同 不变
struct file_operations 同 但 ioctl 必须用 unlocked_ioctl
proc_create(name, mode, parent, fops) proc_create(name, mode, parent, proc_ops) 用 struct proc_ops
create_proc_entry() 已删除 用 proc_create()
create_proc_read_entry() 已删除 用 proc_create() + seq_file
init_timer(&t) + t.function= timer_setup(&t, fn, flags) init_timer 已删除
struct device_driver(device.h) include/linux/device/driver.h 头文件拆分
struct bus_type(device.h) include/linux/device/bus.h 头文件拆分
struct class(device.h) include/linux/device/class.h 头文件拆分
mod_devicetable.h 定义所有 id include/linux/device-id/*.h 头文件拆分
struct miscdevice 无 nodename 新增 nodename/mode 见 include/linux/miscdevice.h

7.2.5 头文件拆分的实际影响 :老代码 #include <linux/device.h> 仍然能编译(它会转发),但新代码应包含具体头文件:

cpp 复制代码
#include <linux/device/class.h>    /* struct class, class_create */
#include <linux/device/driver.h>   /* struct device_driver */
#include <linux/device/bus.h>      /* struct bus_type */
#include <linux/device-id/i2c.h>   /* struct i2c_device_id */

23.4 内核编程通用 API

4.0 7.2.5 说明
strlcpy(dst, src, size) strscpy(dst, src, size) strlcpy 已移除
strncpy strscpy / strscpy_pad
snprintf scnprintf(内核更常用)
init_MUTEX() 已删除 用 mutex_init()
DECLARE_MUTEX 已删除 用 DEFINE_MUTEX
sleep_on() 已删除 用等待队列
interruptible_sleep_on() 已删除 用 wait_event_interruptible()
check_region() 已删除 用 request_region()
get_user_pages() 仍存在,但推荐 get_user_pages_fast() / pin_user_pages*() 见 include/linux/mm.h
手动加锁/解锁 guard(mutex)(&lock)、scoped_guard() include/linux/cleanup.h
手动 kfree __free(kfree) include/linux/cleanup.h
kmalloc + 检查 devm_kzalloc 推荐 devm
WARN_ON 大量使用 WARN_ON_ONCE 优先 避免日志风暴

7.2.5 的 cleanup.h(RAII 风格):

cpp 复制代码
#include <linux/cleanup.h>

/* 自动释放锁 */
guard(mutex)(&my_lock);
/* 出作用域自动 unlock */

/* 自动 kfree */
struct my_data *d __free(kfree) = kzalloc(sizeof(*d), GFP_KERNEL);
/* 出作用域自动 kfree(d) */

/* 作用域内加锁 */
scoped_guard(mutex, &my_lock) {
	...
}

23.5 中断、时钟与并发

4.0 7.2.5 说明
request_irq() request_threaded_irq() / devm_request_irq() 推荐线程化 + devm
tasklet 工作队列 / threaded IRQ tasklet 仅维护旧代码
init_timer() timer_setup() 见 23.3
setup_timer() timer_setup() 已删除
mod_timer() 同
hrtimer 手写 hrtimer_init() + hrtimer_start() 不变
smp_mb__after_atomic() 等 同 内存屏障 API 稳定
atomic_dec_and_test() 同
手写 RCU rcu_read_lock()/synchronize_rcu() 不变
struct completion 同

7.2.5 中断要点:

cpp 复制代码
/* 推荐:devm + threaded */
ret = devm_request_threaded_irq(dev, irq,
                                my_hardirq_handler,   /* 顶半部(可选 NULL) */
                                my_thread_fn,         /* 底半部(可睡眠) */
                                IRQF_ONESHOT, "mydrv", priv);

IRQF_ONESHOT 表示顶半部返回 IRQ_WAKE_THREAD 后, 中断在底半部完成前保持屏蔽。

23.6 网络设备

4.0 7.2.5 说明
init_etherdev() 已删除 用 alloc_etherdev()
alloc_netdev() alloc_netdev() / alloc_etherdev_mqs()
ndo_set_multicast_list ndo_set_rx_mode
ndo_tx_timeout(dev) ndo_tx_timeout(dev, txqueue) 多队列
ethtool get_settings get_link_ksettings
netif_napi_add(dev, napi, poll, weight) netif_napi_add_weight() 或 netif_napi_add()(新签名) 见 netdevice.h
手动 NAPI 调度 napi_schedule() 不变
eth_change_mtu 已删除 用默认 MTU 处理
中断里 netif_rx() napi_gro_receive() GRO
dev_kfree_skb() dev_kfree_skb_irq()(中断中)
单队列 多队列 + netif_set_real_num_tx_queues()
无 XDP XDP + AF_XDP 7.2.5 特性

7.2.5 的 ndo_tx_timeout 签名(这是最常踩的坑):

cpp 复制代码
/* 4.0 */
void my_tx_timeout(struct net_device *dev);

/* 7.2.5 */
void my_tx_timeout(struct net_device *dev, unsigned int txqueue);

23.7 I²C / SPI / USB

4.0 7.2.5 说明
i2c_driver.probe(client, id) probe(client) 少一个参数
i2c_driver.remove 返回 int 返回 void
i2c_new_device() i2c_new_client_device()
i2c_new_probed_device() i2c_new_scanned_device()
i2c_add_adapter() 手动删 devm_i2c_add_adapter() 推荐
裸 SMBus 调用 regmap 推荐
struct spi_master struct spi_controller 重命名
spi_alloc_master() spi_alloc_host()
spi_register_master() spi_register_controller()
spi_master_get/put spi_controller_get/put
spi_driver.probe(spi) 不变(单参数)
spi_driver.remove 返回 int 返回 void
usb_driver.probe(intf, id) 不变(两参数) 注意与 i2c/spi 不同
usb_driver.disconnect 返回 void 不变
usb_buffer_alloc/free 已删除 用 usb_alloc_coherent/free_coherent
usb_alloc_urb 不变
legacy gadget(g_serial 等) configfs + libcomposite
OTG transceiver role switch + Type-C

注意 :usb_driver.probe 在 7.2.5 中仍然是两个参数 , 与 i2c_driver/spi_driver 的单参数 probe 不同。这是最容易搞混的地方。

23.8 GPIO / pinctrl / 时钟 / 电源

4.0 7.2.5 说明
gpio_request() devm_gpiod_get() 整数接口在 CONFIG_GPIOLIB_LEGACY=n 时不可用
gpio_get_value() gpiod_get_value()
gpio_set_value() gpiod_set_value()
gpio_direction_output() gpiod_direction_output()
of_get_named_gpio() devm_gpiod_get()
include <linux/gpio.h> <linux/gpio/consumer.h> (消费者) <linux/gpio/driver.h>(驱动) gpio.h 已标注"must not be included"
gpio_request_array() devm_gpiod_get_array()
手动 pinctrl apply 内核自动 pinctrl_bind_pins()
clk_get() + clk_enable() devm_clk_get_enabled()
clk_prepare() 手动 devm_clk_get_prepared()
regulator_get() + enable devm_regulator_get_enable()
dev_pm_opp_set_regulator() devm_pm_opp_set_config()
pm_qos_add_request() cpu_latency_qos_add_request()
pm_runtime_get_sync() pm_runtime_resume_and_get() 失败自动回退
pm_runtime_enable() 手动 disable devm_pm_runtime_enable()
手写 dev_pm_ops DEFINE_SIMPLE_DEV_PM_OPS() 一行

GPIO 头文件的现代规则(源码里写得很明确):

场景 应包含
实现 GPIO 控制器 <linux/gpio/driver.h>
使用 GPIO(消费者) <linux/gpio/consumer.h>
使用遗留整数接口 <linux/gpio/legacy.h>(且需 CONFIG_GPIOLIB_LEGACY)

23.9 块设备

4.0 7.2.5 说明
blk_init_queue() 已删除 用 blk-mq
struct request_queue 手动 blk_mq_alloc_tag_set() + blk_mq_alloc_disk()
make_request_fn 已删除 用 blk_mq_ops.queue_rq
elv_add_request() 已删除
blk_queue_make_request() 用 blk_alloc_disk() 或 blk_mq_alloc_disk()
add_disk() add_disk() / device_add_disk()
bio_endio() 同
submit_bio() 同
struct block_device_operations 同(submit_bio 成员可用)

7.2.5 的现代块设备骨架:

cpp 复制代码
struct blk_mq_tag_set tag_set = {
	.ops = &my_mq_ops,
	.nr_hw_queues = 1,
	.queue_depth = 128,
	.numa_node = NUMA_NO_NODE,
};
blk_mq_alloc_tag_set(&tag_set);
disk = blk_mq_alloc_disk(&tag_set, NULL);
disk->major = MY_MAJOR;
disk->first_minor = 0;
disk->fops = &my_fops;
disk->private_data = priv;
strscpy(disk->disk_name, "myblk", DISK_NAME_LEN);
set_capacity(disk, MY_SECTORS);
add_disk(disk);

23.10 迁移检查清单

从 4.0 代码迁移到 7.2.5 时,按此清单逐项检查:

  • init_timer() → timer_setup()
  • create_proc_entry() → proc_create() + struct proc_ops
  • class_create(owner, name) → class_create(name)
  • strlcpy → strscpy
  • i2c_driver.probe 改单参数,remove 改 void
  • spi_master → spi_controller,spi_alloc_master → spi_alloc_host
  • spi_driver.remove 改 void
  • ndo_tx_timeout 加 txqueue 参数
  • netif_napi_add 检查新签名
  • 整数 GPIO → gpiod_* 描述符
  • pm_qos_* → cpu_latency_qos_*
  • pm_runtime_get_sync → pm_runtime_resume_and_get
  • dma_request_slave_channel → dma_request_chan
  • dmaengine_terminate_all → dmaengine_terminate_sync
  • blk_init_queue → blk-mq
  • platform_driver.remove 返回 void
  • 检查所有 remove 是否还有 int 返回值
  • 用 checkpatch.pl 检查风格
  • 在 QEMU 上跑一遍(第 22 章)

23.11 常见错误

错误 现象 修正
用 -C 且假设 cwd 不变 路径错 用 -f 或显式 cd
class_create 两参数 编译错误 单参数
proc_ops 用 fops 成员名 编译错误 用 proc_open/proc_read
strlcpy 链接错误 strscpy
init_timer 编译错误 timer_setup
platform_driver.remove 返回 int 编译错误 返回 void
i2c probe 两参数 编译错误 单参数
usb probe 一参数 编译错误 仍是两参数
spi_master 编译警告 spi_controller
用整数 GPIO 编译失败 gpiod_*
pm_qos_add_request 编译失败 cpu_latency_qos_*
blk_init_queue 编译失败 blk-mq
忘记 fallthrough; 警告变错误 显式标注

23.12 本章实验清单

实验 目的 关键观察
编译 4.0 风格模块 看编译错误 逐条对照本章修复
字符设备迁移 掌握 class/proc 变化 编译通过
I²C 驱动迁移 掌握 probe 签名 在 i2c-stub 上 probe
网络驱动迁移 掌握 NAPI/ethtool ip link 正常
GPIO 迁移 掌握描述符 gpiod_* 工作
PM 迁移 掌握 PM QoS/Runtime PM runtime_status
checkpatch 风格检查 无 error

23.13 总结

  1. 4.0 → 7.2.5 最大变化是硬件描述从板级文件转向设备树/ACPI。
  2. 大量旧 API 被删除 而非仅弃用:init_timer、create_proc_entry、 strlcpy、init_MUTEX、sleep_on、check_region、usb_buffer_alloc、 blk_init_queue、init_etherdev 等。
  3. probe/remove 签名 是高频踩坑点: i2c_driver/spi_driver 是单参数 probe + void remove, usb_driver 仍是两参数 probe。
  4. GPIO 描述符 API 全面取代整数 GPIO;linux/gpio.h 已标注禁止包含。
  5. regmap、devm_、cleanup.h(guard/__free) 是现代驱动的三大便利设施。
  6. blk-mq 是块设备的唯一路径;请求队列旧接口全部删除。
  7. 构建侧:O= 外部构建 、-f $KDIR/Makefile M=$PWD 、 LLVM=1 、配置片段合并 是 7.2.5 的标准做法。
  8. 迁移后用 checkpatch.pl + QEMU 实验 双重验证(第 21、22 章)。
相关推荐
Snasph11 天前
01-设备驱动概述与开发环境构建
linux 驱动学习
Snasph11 天前
05-Linux 文件系统与设备文件
linux 驱动学习
Snasph11 天前
04-Linux 内核模块
linux 驱动学习