inside rtl8139

「inside rtl8139」の編集履歴(バックアップ)一覧はこちら

inside rtl8139」(2008/04/15 (火) 20:29:30) の最新版変更点

追加された行は緑色になります。

削除された行は赤色になります。

*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 } }} **次はこのあたり #highlight(C){{ 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(); }} -spin_lock_irqsave -RTL_W32_F -wmb ですね。 ***RTL_W32_F() -8139too.c 同じソース内に定義がありました。 648 #define RTL_W32_F(reg, val32) do { iowrite32 ((val32), ioaddr + (reg)); ioread32 (ioaddr + (reg)); } while (0) -iowrite -ioread この二つの関数で構成されています。 ****iowrite -/src/linux-2.6.18/lib/iomap.c 91 void fastcall iowrite32(u32 val, void __iomem *addr) 92 { 93 IO_COND(addr, outl(val,port), writel(val, addr)); 94 } ↓ -/src/linux-2.6.18/lib/iomap.c 42 #define IO_COND(addr, is_pio, is_mmio) do { \ 43 unsigned long port = (unsigned long __force)addr; \ 44 if (port < PIO_RESERVED) { \ 45 VERIFY_PIO(port); \ 46 port &= PIO_MASK; \ 47 is_pio; \ 48 } else { \ 49 is_mmio; \ 50 } \ 51 } while (0) 渡されたアドレスがPIO_RESERVEDより小さいときはoutlが実行される。 そうではないときはwritelが実行される。 ****writel -/src/linux-2.6.18/include/asm-i386/io.h 188 static inline void writel(unsigned int b, volatile void __iomem *addr) 189 { 190 *(volatile unsigned int __force *) addr = b; 191 } ****outl -src/linux-2.6.18/include/asm-i386/io.h 334 #define BUILDIO(bwl,bw,type) \ 335 static inline void out##bwl##_local(unsigned type value, int port) { \ 336 __asm__ __volatile__("out" #bwl " %" #bw "0, %w1" : : "a"(value), "Nd"(port)); \ 337 } \ 338 static inline unsigned type in##bwl##_local(int port) { \ 339 unsigned type value; \ 340 __asm__ __volatile__("in" #bwl " %w1, %" #bw "0" : "=a"(value) : "Nd"(port)); \ 341 return value; \ 342 } \ 343 static inline void out##bwl##_local_p(unsigned type value, int port) { \ 344 out##bwl##_local(value, port); \ 345 slow_down_io(); \ 346 } \ 347 static inline unsigned type in##bwl##_local_p(int port) { \ 348 unsigned type value = in##bwl##_local(port); \ 349 slow_down_io(); \ 350 return value; \ 351 } \ 352 __BUILDIO(bwl,bw,type) \ 353 static inline void out##bwl##_p(unsigned type value, int port) { \ 354 out##bwl(value, port); \ 355 slow_down_io(); \ 356 } \ 357 static inline unsigned type in##bwl##_p(int port) { \ 358 unsigned type value = in##bwl(port); \ 359 slow_down_io(); \ 360 return value; \ 361 } \ 362 static inline void outs##bwl(int port, const void *addr, unsigned long count) { \ 363 __asm__ __volatile__("rep; outs" #bwl : "+S"(addr), "+c"(count) : "d"(port)); \ 364 } \ 365 static inline void ins##bwl(int port, void *addr, unsigned long count) { \ 366 __asm__ __volatile__("rep; ins" #bwl : "+D"(addr), "+c"(count) : "d"(port)); \ 367 } 368 369 BUILDIO(b,b,char) 370 BUILDIO(w,w,short) 371 BUILDIO(l,,int) 372 プリプロセッサを通すと、下のようなものになります。 static inline void outl_local(unsigned int value, int port) { __asm__ __volatile__("out" "l" " %" "" "0, %w1" : : "a"(value), "Nd"(port)); } static inline unsigned int inl_local(int port) { unsigned int value; __asm__ __volatile__("in" "l" " %w1, %" "" "0" : "=a"(value) : "Nd"(port)); return value; } static inline void outl_local_p(unsigned int value, int port) { outl_local(value, port); slow_down_io(); } static inline unsigned int inl_local_p(int port) { unsigned int value = inl_local(port); slow_down_io(); return value; } static inline void outl(unsigned int value, int port) { outl_local(value, port); } static inline unsigned int inl(int port) { return inl_local(port); } static inline void outl_p(unsigned int value, int port) { outl(value, port); slow_down_io(); } static inline unsigned int inl_p(int port) { unsigned int value = inl(port); slow_down_io(); return value; } static inline void outsl(int port, const void *addr, unsigned long count) { __asm__ __volatile__("rep; outs" "l" : "+S"(addr), "+c"(count) : "d"(port)); } static inline void insl(int port, void *addr, unsigned long count) { __asm__ __volatile__("rep; ins" "l" : "+D"(addr), "+c"(count) : "d"(port)); } inlなどの定義も混じってるけど気にしない。 結局のところインラインアセンブリのOUT系の命令になるようです。 PIOなどの定義は↓ -/src/linux-2.6.18/lib/iomap.c 24 #ifndef HAVE_ARCH_PIO_SIZE 25 /* 26 * We encode the physical PIO addresses (0-0xffff) into the 27 * pointer by offsetting them with a constant (0x10000) and 28 * assuming that all the low addresses are always PIO. That means 29 * we can do some sanity checks on the low bits, and don't 30 * need to just take things for granted. 31 */ 32 #define PIO_OFFSET 0x10000UL 33 #define PIO_MASK 0x0ffffUL 34 #define PIO_RESERVED 0x40000UL 35 #endif 英文によれば、低アドレスは全部PIO用にしておくので細かくチェックしなくても良い。らしいです。
*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 } }} **次はこのあたり #highlight(C){{ 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(); }} -spin_lock_irqsave -RTL_W32_F -wmb ですね。 ***RTL_W32_F() -8139too.c 同じソース内に定義がありました。 648 #define RTL_W32_F(reg, val32) do { iowrite32 ((val32), ioaddr + (reg)); ioread32 (ioaddr + (reg)); } while (0) -iowrite -ioread この二つの関数で構成されています。ioread,iowriteは同じような命令に帰着するのでwriteだけしか調べません。 ****iowrite -/src/linux-2.6.18/lib/iomap.c 91 void fastcall iowrite32(u32 val, void __iomem *addr) 92 { 93 IO_COND(addr, outl(val,port), writel(val, addr)); 94 } ↓ -/src/linux-2.6.18/lib/iomap.c 42 #define IO_COND(addr, is_pio, is_mmio) do { \ 43 unsigned long port = (unsigned long __force)addr; \ 44 if (port < PIO_RESERVED) { \ 45 VERIFY_PIO(port); \ 46 port &= PIO_MASK; \ 47 is_pio; \ 48 } else { \ 49 is_mmio; \ 50 } \ 51 } while (0) 渡されたアドレスがPIO_RESERVEDより小さいときはoutlが実行される。 そうではないときはwritelが実行される。 ****writel -/src/linux-2.6.18/include/asm-i386/io.h 188 static inline void writel(unsigned int b, volatile void __iomem *addr) 189 { 190 *(volatile unsigned int __force *) addr = b; 191 } ****outl -src/linux-2.6.18/include/asm-i386/io.h 334 #define BUILDIO(bwl,bw,type) \ 335 static inline void out##bwl##_local(unsigned type value, int port) { \ 336 __asm__ __volatile__("out" #bwl " %" #bw "0, %w1" : : "a"(value), "Nd"(port)); \ 337 } \ 338 static inline unsigned type in##bwl##_local(int port) { \ 339 unsigned type value; \ 340 __asm__ __volatile__("in" #bwl " %w1, %" #bw "0" : "=a"(value) : "Nd"(port)); \ 341 return value; \ 342 } \ 343 static inline void out##bwl##_local_p(unsigned type value, int port) { \ 344 out##bwl##_local(value, port); \ 345 slow_down_io(); \ 346 } \ 347 static inline unsigned type in##bwl##_local_p(int port) { \ 348 unsigned type value = in##bwl##_local(port); \ 349 slow_down_io(); \ 350 return value; \ 351 } \ 352 __BUILDIO(bwl,bw,type) \ 353 static inline void out##bwl##_p(unsigned type value, int port) { \ 354 out##bwl(value, port); \ 355 slow_down_io(); \ 356 } \ 357 static inline unsigned type in##bwl##_p(int port) { \ 358 unsigned type value = in##bwl(port); \ 359 slow_down_io(); \ 360 return value; \ 361 } \ 362 static inline void outs##bwl(int port, const void *addr, unsigned long count) { \ 363 __asm__ __volatile__("rep; outs" #bwl : "+S"(addr), "+c"(count) : "d"(port)); \ 364 } \ 365 static inline void ins##bwl(int port, void *addr, unsigned long count) { \ 366 __asm__ __volatile__("rep; ins" #bwl : "+D"(addr), "+c"(count) : "d"(port)); \ 367 } 368 369 BUILDIO(b,b,char) 370 BUILDIO(w,w,short) 371 BUILDIO(l,,int) 372 プリプロセッサを通すと、下のようなものになります。 static inline void outl_local(unsigned int value, int port) { __asm__ __volatile__("out" "l" " %" "" "0, %w1" : : "a"(value), "Nd"(port)); } static inline unsigned int inl_local(int port) { unsigned int value; __asm__ __volatile__("in" "l" " %w1, %" "" "0" : "=a"(value) : "Nd"(port)); return value; } static inline void outl_local_p(unsigned int value, int port) { outl_local(value, port); slow_down_io(); } static inline unsigned int inl_local_p(int port) { unsigned int value = inl_local(port); slow_down_io(); return value; } static inline void outl(unsigned int value, int port) { outl_local(value, port); } static inline unsigned int inl(int port) { return inl_local(port); } static inline void outl_p(unsigned int value, int port) { outl(value, port); slow_down_io(); } static inline unsigned int inl_p(int port) { unsigned int value = inl(port); slow_down_io(); return value; } static inline void outsl(int port, const void *addr, unsigned long count) { __asm__ __volatile__("rep; outs" "l" : "+S"(addr), "+c"(count) : "d"(port)); } static inline void insl(int port, void *addr, unsigned long count) { __asm__ __volatile__("rep; ins" "l" : "+D"(addr), "+c"(count) : "d"(port)); } inlなどの定義も混じってるけど気にしない。 結局のところインラインアセンブリのOUT系の命令になるようです。 PIOなどの定義は↓ -/src/linux-2.6.18/lib/iomap.c 24 #ifndef HAVE_ARCH_PIO_SIZE 25 /* 26 * We encode the physical PIO addresses (0-0xffff) into the 27 * pointer by offsetting them with a constant (0x10000) and 28 * assuming that all the low addresses are always PIO. That means 29 * we can do some sanity checks on the low bits, and don't 30 * need to just take things for granted. 31 */ 32 #define PIO_OFFSET 0x10000UL 33 #define PIO_MASK 0x0ffffUL 34 #define PIO_RESERVED 0x40000UL 35 #endif 英文によれば、低アドレスは全部PIO用にしておくので細かくチェックしなくても良い。らしいです。

表示オプション

横に並べて表示:
変化行の前後のみ表示:
ツールボックス

下から選んでください:

新しいページを作成する
ヘルプ / FAQ もご覧ください。