本文共 5257 字,大约阅读时间需要 17 分钟。
老大提了新需求:
板子上2个PCI网卡,2个CPU自带TSEC;
要求TSEC对应 eth0,eth1, PCI网卡对应 eth2,eth3
网络设备的注册是由
register_netdev(struct net_device *dev)来实现的
网络设备驱动加载的时候,会调这个函数,到时就会确定网络设备的接口名称为 eth1或2或3...
在设备驱动代码里强行指定设备接口名?麻烦,改的地方多
忽然想到了驱动加载顺序,看了看 do_initcall,顺便网上查了查 __define_initcall(level,fn)
然后改了下/drivers/net/Makefile
- obj-$(CONFIG_GIANFAR) += gianfar_driver.o #wanghui modify netdev init sequence 2011-03-10
- #(tesc0,tesc1 ===> eth0,eth1 pci0,pci1 ==> eth2,eth3)
- obj-$(CONFIG_E1000) += e1000/
- obj-$(CONFIG_E1000E) += e1000e/
- obj-$(CONFIG_IBM_EMAC) += ibm_emac/
- obj-$(CONFIG_IBM_NEW_EMAC) += ibm_newemac/
- obj-$(CONFIG_IXGBE) += ixgbe/
- obj-$(CONFIG_IXGB) += ixgb/
- obj-$(CONFIG_IP1000) += ipg.o
- obj-$(CONFIG_CHELSIO_T1) += chelsio/
- obj-$(CONFIG_CHELSIO_T3) += cxgb3/
- obj-$(CONFIG_EHEA) += ehea/
- obj-$(CONFIG_CAN) += can/
- obj-$(CONFIG_BONDING) += bonding/
- obj-$(CONFIG_ATL1) += atl1/
- #obj-$(CONFIG_GIANFAR) += gianfar_driver.o # wanghui del 2011-03-10
- obj-$(CONFIG_TEHUTI) += tehuti.o
编译,搞定。--------------------------- 华丽的分割线 -------------------------------------前言 宏定义__define_initcall(level,fn)对于内核的初始化很重要,它指示 编译器在编译的时候,将一系列初始化函数的起始地址值按照一定的顺序 放在一个section中。在内核初始化阶段,do_initcalls() 将按顺序从该 section中以函数指针的形式取出这些函数的起始地址,来依次完成相应 的初始化。由于内核某些部分的初始化需要依赖于其他某些部分的初始化 的完成,因此这个顺序排列常常非常重要。 下面将从__define_initcall(level,fn) 宏定义的代码分析入手,依次 分析名称为initcall.init的section的结构,最后分析内核初始化函数 do_initcalls()是如何利用宏定义__define_initcall(level,fn)及其相 关的衍生的7个宏宏定义,来实现内核某些部分的顺序初始化的。1、分析 __define_initcall(level,fn) 宏定义 1) 这个宏的定义位于inlclude\linux\init.h中: #define __define_initcall(level,fn) \ static initcall_t __initcall_##fn \ __attribute__((__section__(".initcall" level ".init"))) \ = fn 其中 initcall_t 是一个函数指针类型: typedef int (*initcall_t)(void); 而属性 __attribute__((__section__())) 则表示把对象放在一个这个 由括号中的名称所指代的section中。 所以这个宏定义的的含义是:1) 声明一个名称为__initcall_##fn的函数 指针(其中##表示替换连接,);2) 将这个函数指针初始化为fn;3) 编译 的时候需要把这个函数指针变量放置到名称为 ".initcall" level ".init" 的section中(比如level="1",代表这个section的名称是 ".initcall1.init")。 2) 举例:__define_initcall(6, pci_init) 上述宏调用的含义是:1) 声明一个函数指针__initcall_pic_init = pci_init; 且 2) 这个指针变量__initcall_pic_init 需要放置到名称为 .initcall6.init 的section中( 其实质就是将 这个函数pic_init的首地址放置到了这个 section中)。 3) 这个宏一般并不直接使用,而是被定义成下述其他更简单的7个衍生宏 这些衍生宏宏的定义也位于 inlclude\linux\Init.h 中: #define core_initcall(fn) __define_initcall("1",fn) #define postcore_initcall(fn) __define_initcall("2",fn) #define arch_initcall(fn) __define_initcall("3",fn) #define subsys_initcall(fn) __define_initcall("4",fn) #define fs_initcall(fn) __define_initcall("5",fn) #define device_initcall(fn) __define_initcall("6",fn) #define late_initcall(fn) __define_initcall("7",fn) 因此通过宏 core_initcall() 来声明的函数指针,将放置到名称为 .initcall1.init的section中,而通过宏 postcore_initcall() 来 声明的函数指针,将放置到名称为.initcall2.init的section中, 依次类推。 4) 举例:device_initcall(pci_init) 解释同上 1-2)。2、与初始化调用有关section--initcall.init被分成了7个子section 1) 它们依次是.initcall1.init、.initcall2.init、...、.initcall7.init 2) 按照先后顺序依次排列 3) 它们的定义在文件vmlinux.lds.S中 例如 对于i386+,在i386\kernel\vmlinux.lds.S中有: __initcall_start = .; .initcall.init : { *(.initcall1.init) *(.initcall2.init) *(.initcall3.init) *(.initcall4.init) *(.initcall5.init) *(.initcall6.init) *(.initcall7.init) } __initcall_end = .; 而在makefile 中有 LDFLAGS_vmlinux += -T arch/$(ARCH)/kernel/vmlinux.lds.s 4) 在这7个section总的开始位置被标识为__initcall_start, 而在结尾被标识为__initcall_end。3、 内核初始化函数do_basic_setup(): do_initcalls() 将从.initcall.init 中,也就是这7个section中依次取出所有的函数指针,并调用这些 函数指针所指向的函数,来完成内核的一些相关的初始化。 这个函数的定义位于init\main.c中: extern initcall_t __initcall_start, __initcall_end; static void __init do_initcalls(void) { initcall_t *call; .... for (call = &__initcall_start; call < &__initcall_end; call++) { .... (*call)(); .... } .... } 这些函数指针指向的函数就是通过宏__define_initcall(level,fn) 赋值的函数fn,他们调用的顺序就是放置在这些section中的顺序, 这个顺序很重要, 这就是这个宏__define_initcall(level,fn)的作用。 注意到,这里__initcall_start 和 __initcall_end 就是section initcall.init的头和尾。4、 归纳之 1) __define_initcall(level,fn)的作用就是指示编译器把一些初始化函数 的指针(即:函数起始地址)按照顺序放置一个名为 .initcall.init 的 section中,这个section又被分成了7个子section,它们按顺序排列。 在内核初始化阶段,这些放置到这个section中的函数指针将供 do_initcalls() 按顺序依次调用,来完成相应初始化。 2) 函数指针放置到的子section由宏定义的level确定,对应level较小的 子section位于较前面。而位于同一个子section内的函数指针顺序不定, 将由编译器按照编译的顺序随机指定。 3) 因此,如果你希望某个初始化函数在内核初始化阶段就被调用,那么你 就应该使用宏__define_initcall(level,fn) 或 其7个衍生宏 把这个 函数fn的对应的指针放置到按照初始化的顺序放置到相关的 section 中。 同事,如果某个初始化函数fn_B需要依赖于另外一个初始化函数fn_A的 完成,那么你应该把fn_B放在比fn_A对应的level值较大的子section中, 这样,do_initcalls()将在fn_A之后调用fn_B。***********************************************************************如果你希望某个初始化函数在内核初始化阶段就被调用,那么你就应该使用宏__define_initcall(level,fn) 或 其7个衍生宏来把这个初始化函数fn的起始地址按照初始化的顺序放置到相关的section 中。 内核初始化时的do_initcalls()将从这个section中按顺序找到这些函数来执行。 如果你希望某个初始化函数在内核初始化阶段就被调用,那么你就应该使用宏__define_initcall(level,fn) 或 其7个衍生宏来把这个初始化函数fn的起始地址按照初始化的顺序放置到相关的section 中。 内核初始化时的do_initcalls()将从这个section中按顺序找到这些函数来执行。******************************************************************* <script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];</script>
阅读(31) | 评论(0) | 转发(0) |
转载地址:http://dsuws.baihongyu.com/