對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 modules
make: *** /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 modules
make[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 2
make[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 modules
make[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 1
make[1]: *** [_module_/home/xiebiwei/dev/code/Ldd/snull] Error 2
make[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。
二 正確的snull編譯方法
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
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);
+ }
+
}