「Linux Kernel」の編集履歴(バックアップ)一覧はこちら
「Linux Kernel」(2008/04/12 (土) 19:22:05) の最新版変更点
追加された行は緑色になります。
削除された行は赤色になります。
*Linux Kernel
Linuxカーネルについてまなんだことを書きます。
Linuxカーネルというくくりでいいのかよくわからないことも書きます。
Linuxカーネルに付随するいくつかのことも書きます。
**関数
***outb_p(char addr , port)
portにaddrを書き込みます。
これをしたあとにinb_p(port)とかやったりします。
manページには以下のように書いてあります。
the _p-suffix functions pause until the I/O completes
I/Oが完了するまで待つとありますね。
RTCの読み込みとかに使います。
makeした.oファイルを逆アセンブルすると、
outb_p (addr , 0x70 )
が
out %al,$0x70
call *0xc8
になったりします。
call *0xc8
ってなんでしょうね。
なにか待つだけのサブルーチンなんでしょうかね。
と思ってテスト用のデバイスドライバを書いてコンパイルしたものをを逆アセンブルしてみたら、上のcallの部分が
out %al,0x80
になっていました。もうちょっと調べたら、マクロ定義がio.hにありました。
00028 #ifdef SLOW_IO_BY_JUMPING
00029 #define __SLOW_DOWN_IO __asm__ __volatile__("jmp 1f\n1:\tjmp 1f\n1:")
00030 #else
00031 #define __SLOW_DOWN_IO __asm__ __volatile__("outb %al,$0x80")
00032 #endif
最終的には
outb_p (addr , 0x70 (
が
out %al,$0x70
out %al,0x80
になるようです。
**rtl8139(蟹チップ)のドライバ
パケット送信について調べてみた。
ドライバのソースは
/drivers/net/8139too.c
パケット送信は以下の関数で行われている模様。
626 static int rtl8139_start_xmit (struct sk_buff *skb,
627 struct net_device *dev);
その関数は以下の様になっています。
#highlight(C){{
1706 static int rtl8139_start_xmit (struct sk_buff *skb, struct net_device *dev)
1707 {
1708 struct rtl8139_private *tp = netdev_priv(dev);
1709 void __iomem *ioaddr = tp->mmio_addr;
1710 unsigned int entry;
1711 unsigned int len = skb->len;
1712 unsigned long flags;
1713
1714 /* Calculate the next Tx descriptor entry. */
1715 entry = tp->cur_tx % NUM_TX_DESC;
1716
1717 /* Note: the chip doesn't have auto-pad! */
1718 if (likely(len < TX_BUF_SIZE)) {
1719 if (len < ETH_ZLEN)
1720 memset(tp->tx_buf[entry], 0, ETH_ZLEN);
1721 skb_copy_and_csum_dev(skb, tp->tx_buf[entry]);
1722 dev_kfree_skb(skb);
1723 } else {
1724 dev_kfree_skb(skb);
1725 tp->stats.tx_dropped++;
1726 return 0;
1727 }
1728
1729 spin_lock_irqsave(&tp->lock, flags);
1730 RTL_W32_F (TxStatus0 + (entry * sizeof (u32)),
1731 tp->tx_flag | max(len, (unsigned int)ETH_ZLEN));
1732
1733 dev->trans_start = jiffies;
1734
1735 tp->cur_tx++;
1736 wmb();
1737
1738 if ((tp->cur_tx - NUM_TX_DESC) == tp->dirty_tx)
1739 netif_stop_queue (dev);
1740 spin_unlock_irqrestore(&tp->lock, flags);
1741
1742 if (netif_msg_tx_queued(tp))
1743 printk (KERN_DEBUG "%s: Queued Tx packet size %u to slot %d.\n",
1744 dev->name, len, entry);
1745
1746 return 0;
1747 }
}}
関数の実態を探ります。まずはこのあたり。
#highlight(C){{
1718 if (likely(len < TX_BUF_SIZE)) {
1719 if (len < ETH_ZLEN)
1720 memset(tp->tx_buf[entry], 0, ETH_ZLEN);
1721 skb_copy_and_csum_dev(skb, tp->tx_buf[entry]);
1722 dev_kfree_skb(skb);
1723 } else {
1724 dev_kfree_skb(skb);
1725 tp->stats.tx_dropped++;
1726 return 0;
1727 }
}}
***likely()
-/src/linux-2.6.18/include/linux/compiler.h
62 #define likely(x) __builtin_expect(!!(x), 1)
この先はありません。今の僕には理解できませんでした。
***memset()
-/src/linux-2.6.18/arch/i386/lib/memcpy.c
17 void *memset(void *s, int c, size_t count)
18 {
19 return __memset(s, c, count);
20 }
↓
-/src/linux-2.6.18/include/asm-i386/string.h
462 #define __memset(s, c, count) \
463 (__builtin_constant_p(count) ? \
464 __constant_count_memset((s),(c),(count)) : \
465 __memset_generic((s),(c),(count)))
( A ? B : C)の形の3項演算子ですね。Aが真ならB、そうでないならCという感じだったと思います。
****__builtin_constant_p()
GNUコンパイラの組み込み関数だそうです。これも今の僕には理解できませんでした。こちらに詳しく載っています。
[[参考URL>>http://caspar.hazymoon.jp/OpenBSD/docs/gcc-j/Other-Builtins.html]]
****__constant_count_memset()
-/src/linux-2.6.18/include/asm-i386/string.h
361 /* we might want to write optimized versions of these later */
362 #define __constant_count_memset(s,c,count) __memset_generic((s),(c),(count))
****__memset_generic()
-/src/linux-2.6.18/include/asm-i386/string.h
349 static inline void * __memset_generic(void * s, char c,size_t count)
350 {
351 int d0, d1;
352 __asm__ __volatile__(
353 "rep\n\t"
354 "stosb"
355 : "=&c" (d0), "=&D" (d1)
356 :"a" (c),"1" (s),"0" (count)
357 :"memory");
358 return s;
359 }
ということでmemsetはインラインアセンブラになるみたいですね。
インラインアセンブラの制約条件などについては[[こちら>http://yashiromann.sakura.ne.jp/memo/GCC-Inline-Assembly-HOWTO.html#ss6.1]]に詳しく書いてあります。
[[http://yashiromann.sakura.ne.jp/memo/GCC-Inline-Assembly-HOWTO.html#ss6.1>http://yashiromann.sakura.ne.jp/memo/GCC-Inline-Assembly-HOWTO.html#ss6.1]]
***skb_copy_and_csum_dev()
-/src/linux-2.6.18/net/core/skbuff.c
1396 void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to)
1397 {
1398 unsigned int csum;
1399 long csstart;
1400
1401 if (skb->ip_summed == CHECKSUM_HW)
1402 csstart = skb->h.raw - skb->data;
1403 else
1404 csstart = skb_headlen(skb);
1405
1406 BUG_ON(csstart > skb_headlen(skb));
1407
1408 memcpy(to, skb->data, csstart);
1409
1410 csum = 0;
1411 if (csstart != skb->len)
1412 csum = skb_copy_and_csum_bits (skb, csstart, to + csstart,
1413 skb->len - csstart, 0);
1414
1415 if (skb->ip_summed == CHECKSUM_HW) {
1416 long csstuff = csstart + skb->csum;
1417
1418 *((unsigned short *)(to + sstuff)) = csum_fold(csum);
1419 }
1420 }
です。関数としては
-BUG_ON
-memcpy
-skb_copy_and_csum_bits
-csum_fold
ですね。
****BUG_ON()
探したら定義が2つあった。
BUG_ON()一つ目。
-/src/linux-2.6.18/include/asm-generic/bug.h
14 #ifndef HAVE_ARCH_BUG_ON
15 #define BUG_ON(condition) do { if (unlikely((condition)!=0)) BUG(); } while(0)
16 #endif
中では
-unlikely()
-BUG()
が実行されています。BUG()も複数定義があるのでよくわかりません。
一応二つの定義をこぴぺしておきます。
*****BUG()
一つ目。
-/src/linux-2.6.18/include/asm-generic/bug.h
6 #ifdef CONFIG_BUG
7 #ifndef HAVE_ARCH_BUG
8 #define BUG() do { \
9 printk("BUG: failure at %s:%d/%s()!\n", __FILE__, __LINE__, __FUNCTION__); \
10 panic("BUG!"); \
11 } while (0)
12 #endif
二つ目。
-/src/linux-2.6.18/include/asm-generic/bug.h
27 #else /* !CONFIG_BUG */
28 #ifndef HAVE_ARCH_BUG
29 #define BUG()
30 #endif
BUG_ON()2つ目。
-/src/linux-2.6.18/include/asm-generic/bug.h
32 #ifndef HAVE_ARCH_BUG_ON
33 #define BUG_ON(condition) do { if (condition) ; } while(0)
34 #endif
なんかコンパイルフラグですね。よくわかりません。
****memcpy()
-/src/linux-2.6.18/arch/i386/lib/memcpy.c
7 void *memcpy(void *to, const void *from, size_t n)
8 {
9 #ifdef CONFIG_X86_USE_3DNOW
10 return __memcpy3d(to, from, n);
11 #else
12 return __memcpy(to, from, n);
13 #endif
14 }
コンパイルフラグですね。
-__memcpy3d
-__memcpy
の二つです。
*****__memcpy3d()
-/src/linux-2.6.18/include/asm-i386/string.h
300 static __inline__ void *__memcpy3d(void *to, const void *from, size_t len)
301 {
302 if (len < 512)
303 return __memcpy(to, from, len);
304 return _mmx_memcpy(to, from, len);
305 }
_mmx_memcpy()は結構長いインラインアセンブラでした。
-src/linux-2.6.18/arch/i386/lib/mmx.c
にありあます。長いので省きます。コピーをする長さで条件分岐しているんですね。mmxはmulti media extentionの略らしいです。
*****__memcpy()
-/src/linux-2.6.18/include/asm-i386/string.h
203 static __always_inline void * __memcpy(void * to, const void * from, size_t n)
204 {
205 int d0, d1, d2;
206 __asm__ __volatile__(
207 "rep ; movsl\n\t"
208 "movl %4,%%ecx\n\t"
209 "andl $3,%%ecx\n\t"
210 #if 1 /* want to pay 2 byte penalty for a chance to skip microcoded rep? */
211 "jz 1f\n\t"
212 #endif
213 "rep ; movsb\n\t"
214 "1:"
215 : "=&c" (d0), "=&D" (d1), "=&S" (d2)
216 : "0" (n/4), "g" (n), "1" ((long) to), "2" ((long) from)
217 : "memory");
218 return (to);
219 }
こちらはインラインアセンブラになりますね。
****skb_copy_and_csum_bits()
長い・・・
-/src/linux-2.6.18/net/core/skbuff.c
#highlight(C){{
1317 unsigned int skb_copy_and_csum_bits(const struct sk_buff *skb, int offset,
1318 u8 *to, int len, unsigned int csum)
1319 {
1320 int start = skb_headlen(skb);
1321 int i, copy = start - offset;
1322 int pos = 0;
1323
1324 /* Copy header. */
1325 if (copy > 0) {
1326 if (copy > len)
1327 copy = len;
1328 csum = csum_partial_copy_nocheck(skb->data + offset, to,
1329 copy, csum);
1330 if ((len -= copy) == 0)
1331 return csum;
1332 offset += copy;
1333 to += copy;
1334 pos = copy;
1335 }
1336
1337 for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
1338 int end;
1339
1340 BUG_TRAP(start <= offset + len);
1341
1342 end = start + skb_shinfo(skb)->frags[i].size;
1343 if ((copy = end - offset) > 0) {
1344 unsigned int csum2;
1345 u8 *vaddr;
1346 skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
1347
1348 if (copy > len)
1349 copy = len;
1350 vaddr = kmap_skb_frag(frag);
1351 csum2 = csum_partial_copy_nocheck(vaddr +
1352 frag->page_offset +
1353 offset - start, to,
1354 copy, 0);
1355 kunmap_skb_frag(vaddr);
1356 csum = csum_block_add(csum, csum2, pos);
1357 if (!(len -= copy))
1358 return csum;
1359 offset += copy;
1360 to += copy;
1361 pos += copy;
1362 }
1363 start = end;
1364 }
1365
1366 if (skb_shinfo(skb)->frag_list) {
1367 struct sk_buff *list = skb_shinfo(skb)->frag_list;
1368
1369 for (; list; list = list->next) {
1370 unsigned int csum2;
1371 int end;
1372
1373 BUG_TRAP(start <= offset + len);
1374
1375 end = start + list->len;
1376 if ((copy = end - offset) > 0) {
1377 if (copy > len)
1378 copy = len;
1379 csum2 = skb_copy_and_csum_bits(list,
1380 offset - start,
1381 to, copy, 0);
1382 csum = csum_block_add(csum, csum2, pos);
1383 if ((len -= copy) == 0)
1384 return csum;
1385 offset += copy;
1386 to += copy;
1387 pos += copy;
1388 }
1389 start = end;
1390 }
1391 }
1392 BUG_ON(len);
1393 return csum;
1394 }
1395
}}
これの実態を追いかけるのは面倒だなー。省略。
****csum_fold()
-/src/linux-2.6.18/include/asm-i386/checksum.h
#highlight(c){{
99 static inline unsigned int csum_fold(unsigned int sum)
100 {
101 __asm__(
102 "addl %1, %0 ;\n"
103 "adcl $0xffff, %0 ;\n"
104 : "=r" (sum)
105 : "r" (sum << 16), "0" (sum & 0xffff0000)
106 );
107 return (~sum) >> 16;
108 }
}}
*Linux Kernel
Linuxカーネルについてまなんだことを書きます。
Linuxカーネルというくくりでいいのかよくわからないことも書きます。
Linuxカーネルに付随するいくつかのことも書きます。
**関数
***outb_p(char addr , port)
portにaddrを書き込みます。
これをしたあとにinb_p(port)とかやったりします。
manページには以下のように書いてあります。
the _p-suffix functions pause until the I/O completes
I/Oが完了するまで待つとありますね。
RTCの読み込みとかに使います。
makeした.oファイルを逆アセンブルすると、
outb_p (addr , 0x70 )
が
out %al,$0x70
call *0xc8
になったりします。
call *0xc8
ってなんでしょうね。
なにか待つだけのサブルーチンなんでしょうかね。
と思ってテスト用のデバイスドライバを書いてコンパイルしたものをを逆アセンブルしてみたら、上のcallの部分が
out %al,0x80
になっていました。もうちょっと調べたら、マクロ定義がio.hにありました。
00028 #ifdef SLOW_IO_BY_JUMPING
00029 #define __SLOW_DOWN_IO __asm__ __volatile__("jmp 1f\n1:\tjmp 1f\n1:")
00030 #else
00031 #define __SLOW_DOWN_IO __asm__ __volatile__("outb %al,$0x80")
00032 #endif
最終的には
outb_p (addr , 0x70 (
が
out %al,$0x70
out %al,0x80
になるようです。