对LDD中snull程序,编译的时候会有许多问题,鉴于网上还没有合适的解决办法,做此总结,整理知识。本文在debian6.0上运行通过,内核版本为2.6.32。
学习LDD中网络驱动程序部分,理解snull程序的原理很有必要。snull不依赖于硬件,数据包的收发都属于内存操作,但对整个网络驱动程序原理已经做了很好的阐述。程序并不复杂,相比e100.c;8139too.c;pci-skeleton.c,容易理解的多。作者写这本书的时候尚是2.6.11的年代,与现在内核版本相比,有些接口发生了变化,这是snull编译失败的直接原因。本文将描述snull的修改方法,并指出发生变化的接口。本文在debian6.0上运行通过,内核版本为2.6.32。
本文提供了修改完成后的makefile和snull.c,并生成了patch文件。
1)常见问题
2)正确的snull编译方法
3)文件下载
这是我自己遇到的,和网上看到的部分问题,现一一阐述原因,并在第二段(见下文)中阐述解决方法。 如果在这些问题之外,还有问题,清大家留言,方便讨论。至于详细的接口变化,我会另写文章,一一说明。
1)错误描述:
make -C /lib/modules/2.6.32-5-686/build M=/home/xiebiwei/dev/code/Ldd/snull modulesmake: *** /lib/modules/2.6.32-5-686/build: No such file or directory. Stop.make: *** [default] Error 2
问题原因:没有安装内核源代码树,或者是安装了内核源代码树后,没有修改makefile中的kerneldir。
2)错误描述:
make -C /lib/modules/2.6.32/build M=/home/xiebiwei/dev/code/Ldd/snull modulesmake[1]: Entering directory `/usr/src/linux-source-2.6.32'scripts/Makefile.build:49: *** CFLAGS was changed in "/home/xiebiwei/dev/code/Ldd/snull/Makefile". Fix it to use EXTRA_CFLAGS. Stop.make[1]: *** [_module_/home/xiebiwei/dev/code/Ldd/snull] Error 2make[1]: Leaving directory `/usr/src/linux-source-2.6.32'make: *** [default] Error 2
问题原因:内核版本不同,最近版本已经把CFLAGS变为EXTRA_CFLAGS
3)错误描述:
make -C /lib/modules/2.6.32/build M=/home/xiebiwei/dev/code/Ldd/snull modulesmake[1]: Entering directory `/usr/src/linux-source-2.6.32' CC [M] /home/xiebiwei/dev/code/Ldd/snull/snull.o/home/xiebiwei/dev/code/Ldd/snull/snull.c:18:26: error: linux/config.h: No such file or directory/home/xiebiwei/dev/code/Ldd/snull/snull.c: In function ‘snull_poll’:/home/xiebiwei/dev/code/Ldd/snull/snull.c:289: error: ‘struct net_device’ has no member named ‘quota’/home/xiebiwei/dev/code/Ldd/snull/snull.c:289: warning: type defaults to ‘int’ in declaration of ‘_min1’/home/xiebiwei/dev/code/Ldd/snull/snull.c:289: error: ‘struct net_device’ has no member named ‘quota’/home/xiebiwei/dev/code/Ldd/snull/snull.c:319: error: ‘struct net_device’ has no member named ‘quota’/home/xiebiwei/dev/code/Ldd/snull/snull.c:321: error: implicit declaration of function ‘netif_rx_complete’/home/xiebiwei/dev/code/Ldd/snull/snull.c: In function ‘snull_napi_interrupt’:/home/xiebiwei/dev/code/Ldd/snull/snull.c:406: error: implicit declaration of function ‘netif_rx_schedule’/home/xiebiwei/dev/code/Ldd/snull/snull.c: In function ‘snull_init’:/home/xiebiwei/dev/code/Ldd/snull/snull.c:647: error: ‘struct net_device’ has no member named ‘open’/home/xiebiwei/dev/code/Ldd/snull/snull.c:648: error: ‘struct net_device’ has no member named ‘stop’/home/xiebiwei/dev/code/Ldd/snull/snull.c:649: error: ‘struct net_device’ has no member named ‘set_config’/home/xiebiwei/dev/code/Ldd/snull/snull.c:650: error: ‘struct net_device’ has no member named ‘hard_start_xmit’/home/xiebiwei/dev/code/Ldd/snull/snull.c:651: error: ‘struct net_device’ has no member named ‘do_ioctl’/home/xiebiwei/dev/code/Ldd/snull/snull.c:652: error: ‘struct net_device’ has no member named ‘get_stats’/home/xiebiwei/dev/code/Ldd/snull/snull.c:653: error: ‘struct net_device’ has no member named ‘change_mtu’/home/xiebiwei/dev/code/Ldd/snull/snull.c:654: error: ‘struct net_device’ has no member named ‘rebuild_header’/home/xiebiwei/dev/code/Ldd/snull/snull.c:655: error: ‘struct net_device’ has no member named ‘hard_header’/home/xiebiwei/dev/code/Ldd/snull/snull.c:656: error: ‘struct net_device’ has no member named ‘tx_timeout’/home/xiebiwei/dev/code/Ldd/snull/snull.c:659: error: ‘struct net_device’ has no member named ‘poll’/home/xiebiwei/dev/code/Ldd/snull/snull.c:660: error: ‘struct net_device’ has no member named ‘weight’/home/xiebiwei/dev/code/Ldd/snull/snull.c:665: error: ‘struct net_device’ has no member named ‘hard_header_cache’make[2]: *** [/home/xiebiwei/dev/code/Ldd/snull/snull.o] Error 1make[1]: *** [_module_/home/xiebiwei/dev/code/Ldd/snull] Error 2make[1]: Leaving directory `/usr/src/linux-source-2.6.32'make: *** [default] Error 2
问题原因:
1 config.h在2.6.32内核中已经不存在,需要用相应头文件替换
2 struct net_device 的结构在新版本内核中发生了变化,删去了quota成员;将(open,stop,set_config,hard_start_xmit等)封装进了struct net_device_ops;将(poll,weight等)封装进了 struct napi_struct;将(hard_header,hard_header_cache,rebuild等)封装进了struct header_ops。并对部分函数名和参数进行了变动。
3 netif_rx_complete和netif_rx_schedule属于poll机制中的函数,接口名称已经变为napi_complete和napi_schedule。
1)构造内核源代码树
这是编写驱动程序的必要准备,否则会无法编译驱动程序。模块在编译的时候必须调用内核的函数(模块属于内核的一部分,无法调用glibc),故而构造源代码树很必要。
请参考:debian上的详细步骤:http://blog.csdn.net/xiebiwei/archive/2011/02/20/6196818.aspx
debian中下载kernel比较方便,而比较通用的下载kernel方式如下:
用 " uname -r " 查看系统内核版本,到 linux的kernel网站(http://www.kernel.org/pub/linux/kernel/v2.6/ )查找相应版本的内核源文件,下载并将其置于"/usr/src/"下。
接下来便可用上述链接中的方法解压缩内核源代码并编译了。
2)修改MakeFile
I) 将CFLAGS修改为EXTRA_CFLAGS,属于内核升级带来的接口变化。查看
II)修改KERNELDIR为正确的模块编译目录,即构造内核源代码树时生成的目录,一般为" /lib/modules /**/build"。查看
3)修改源文件snull.c
I)头文件中的<linux/config.h>,在新内核中已经被删除,查看老版本内核的该文件内容,并替换。查看
II) 修改 struct snull_priv ,添加struct napi_struct、struct net_device两个结构体的成员变量。2.6.32内核将poll()、weight等封装进了napi_struct,故而想实现轮询机制必须在private中定义napi_struct。 查看
III) 修改函数:snull_poll。2.6.32内核中,struct net_device去除了quota,quota是设备对接收数据包数量的限制;poll函数接口参数也发生了变化,变为了 static int (*poll)(struct napi_struct,int),故而函数体也需要稍做调整,比如priv指针的获取等。查看
IV) 修改函数snull_napi_interrupt,该函数中调度poll的函数接口变为了napi_schedule(struct napi_struct *)。查看
V) 修改函数snull_init。struct net_device的定义发生了变化。查看
将open、stop、set_config等操作封装进了结构体 struct net_device_ops。查看
将harder_header、rebuild_header等操作封装进了struct header_ops;poll则封装进了struct napi_stuct。查看
下载链接 (包括snull.c makefile snull_2.6.32.patch)
使用方法:可以直接覆盖snull.c和makefile。也可以只将patch文件拷贝到snull/下后运行patch -p1 < snull_2.6.32.patch
diff -uNr snull_original/Makefile snull/Makefile--- snull_original/Makefile 2005-01-31 15:31:02.000000000 -0500+++ snull/Makefile 2011-02-24 17:45:39.000000000 -0500@@ -9,8 +9,8 @@ DEBFLAGS = -O2 endif -CFLAGS += $(DEBFLAGS)-CFLAGS += -I..+EXTRA_CFLAGS += $(DEBFLAGS)+EXTRA_CFLAGS += -I.. ifneq ($(KERNELRELEASE),) # call from kernel build system@@ -19,7 +19,7 @@ else -KERNELDIR ?= /lib/modules/$(shell uname -r)/build+KERNELDIR ?= /lib/modules/2.6.32/build PWD := $(shell pwd) default:diff -uNr snull_original/snull.c snull/snull.c--- snull_original/snull.c 2005-01-31 15:31:02.000000000 -0500+++ snull/snull.c 2011-02-26 19:50:06.000000000 -0500@@ -14,8 +14,10 @@ * * $Id: snull.c,v 1.21 2004/11/05 02:36:03 rubini Exp $ */- -#include <linux/config.h>+#ifdef LINUX_CONFIG_H+#define LINUX_CONFIG_H+#include <linux/autoconf.h>+#endif #include <linux/module.h> #include <linux/init.h> #include <linux/moduleparam.h>@@ -87,6 +89,9 @@ u8 *tx_packetdata; struct sk_buff *skb; spinlock_t lock;+ + struct napi_struct napi;+ struct net_device *dev; }; static void snull_tx_timeout(struct net_device *dev);@@ -284,11 +289,12 @@ /* * The poll implementation. */
-static int snull_poll(struct net_device *dev, int *budget)+static int snull_poll(struct napi_struct *napi, int budget) {- int npackets = 0, quota = min(dev->quota, *budget);+ int npackets = 0, quota = budget; struct sk_buff *skb;- struct snull_priv *priv = netdev_priv(dev);+ struct snull_priv *priv = container_of(napi,struct snull_priv,napi);+ struct net_device *dev=priv->dev; struct snull_packet *pkt; while (npackets < quota && priv->rx_queue) {@@ -315,10 +321,8 @@ snull_release_buffer(pkt); } /* If we processed all packets, we're done; tell the kernel and reenable ints */- *budget -= npackets;- dev->quota -= npackets; if (! priv->rx_queue) {- netif_rx_complete(dev);+ napi_complete(&priv->napi); snull_rx_ints(dev, 1); return 0; }@@ -403,7 +407,7 @@ priv->status = 0; if (statusword & SNULL_RX_INTR) { snull_rx_ints(dev, 0); /* Disable further interrupts */- netif_rx_schedule(dev);+ napi_schedule(&priv->napi); } if (statusword & SNULL_TX_INTR) { /* a transmission is over: free the skb */@@ -585,8 +589,8 @@ int snull_header(struct sk_buff *skb, struct net_device *dev,- unsigned short type, void *daddr, void *saddr,- unsigned int len)+ unsigned short type, const void *daddr, const void *saddr,+ unsigned len) { struct ethhdr *eth = (struct ethhdr *)skb_push(skb,ETH_HLEN); @@ -627,6 +631,25 @@ * The init function (sometimes called probe). * It is invoked by register_netdev() */+ +static const struct net_device_ops snull_dev_ops = {++ .ndo_open =snull_open,+ .ndo_stop =snull_release,+ .ndo_set_config =snull_config,+ .ndo_start_xmit =snull_tx,+ .ndo_do_ioctl =snull_ioctl,+ .ndo_get_stats =snull_stats,+ .ndo_change_mtu =snull_change_mtu,+ .ndo_tx_timeout =snull_tx_timeout,+};+ +static const struct header_ops snull_header_ops= {+ .create =snull_header,+ .rebuild =snull_rebuild_header,+ .cache = NULL, /* Disable caching */+};+ void snull_init(struct net_device *dev) { struct snull_priv *priv;@@ -644,25 +667,13 @@ */ ether_setup(dev); /* assign some of the fields */ - dev->open = snull_open;- dev->stop = snull_release;- dev->set_config = snull_config;- dev->hard_start_xmit = snull_tx;- dev->do_ioctl = snull_ioctl;- dev->get_stats = snull_stats;- dev->change_mtu = snull_change_mtu; - dev->rebuild_header = snull_rebuild_header;- dev->hard_header = snull_header;- dev->tx_timeout = snull_tx_timeout;+ dev->netdev_ops = &snull_dev_ops;+ dev->header_ops = &snull_header_ops; dev->watchdog_timeo = timeout;- if (use_napi) {- dev->poll = snull_poll;- dev->weight = 2;- }+ /* keep the default flags, just add NOARP */ dev->flags |= IFF_NOARP; dev->features |= NETIF_F_NO_CSUM;- dev->hard_header_cache = NULL; /* Disable caching */ /* * Then, initialize the priv field. This encloses the statistics@@ -670,9 +681,15 @@ */ priv = netdev_priv(dev); memset(priv, 0, sizeof(struct snull_priv));+ priv->dev = dev;+ spin_lock_init(&priv->lock); snull_rx_ints(dev, 1); /* enable receive interrupts */ snull_setup_pool(dev);+ if (use_napi) {+ netif_napi_add(dev,&priv->napi,snull_poll,2);+ }+ }