diff options
author | Stephen Hemminger <shemminger@linux-foundation.org> | 2007-07-09 18:33:35 -0400 |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2007-07-10 12:22:28 -0400 |
commit | 3cf267539f1f133eb6ba63d074da18cb58cdf89a (patch) | |
tree | c35a52a717702fdade349c1af0d7013bb7c51115 | |
parent | 55d7b4e6ed6ad3ec5e5e30b3b4515a0a6a53e344 (diff) |
sky2: debug interface
Add an optional debug interface for displaying state of transmit/receive
rings. Creates a file debugfs/sky2/ethX for each device that is up.
Signed-off-by: Stephen Hemminger <shemminger@linux-foundation.org>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
-rw-r--r-- | drivers/net/Kconfig | 10 | ||||
-rw-r--r-- | drivers/net/sky2.c | 196 | ||||
-rw-r--r-- | drivers/net/sky2.h | 4 |
3 files changed, 208 insertions, 2 deletions
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 5cc3d517e39b..58c6aa28baff 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig | |||
@@ -2133,6 +2133,16 @@ config SKY2 | |||
2133 | To compile this driver as a module, choose M here: the module | 2133 | To compile this driver as a module, choose M here: the module |
2134 | will be called sky2. This is recommended. | 2134 | will be called sky2. This is recommended. |
2135 | 2135 | ||
2136 | config SKY2_DEBUG | ||
2137 | bool "Debugging interface" | ||
2138 | depends on SKY2 && DEBUG_FS | ||
2139 | help | ||
2140 | This option adds the ability to dump driver state for debugging. | ||
2141 | The file debugfs/sky2/ethX displays the state of the internal | ||
2142 | transmit and receive rings. | ||
2143 | |||
2144 | If unsure, say N. | ||
2145 | |||
2136 | config SK98LIN | 2146 | config SK98LIN |
2137 | tristate "Marvell Yukon Chipset / SysKonnect SK-98xx Support (DEPRECATED)" | 2147 | tristate "Marvell Yukon Chipset / SysKonnect SK-98xx Support (DEPRECATED)" |
2138 | depends on PCI | 2148 | depends on PCI |
diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index f6fe2861cc4c..90b1b9708178 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c | |||
@@ -39,6 +39,7 @@ | |||
39 | #include <linux/workqueue.h> | 39 | #include <linux/workqueue.h> |
40 | #include <linux/if_vlan.h> | 40 | #include <linux/if_vlan.h> |
41 | #include <linux/prefetch.h> | 41 | #include <linux/prefetch.h> |
42 | #include <linux/debugfs.h> | ||
42 | #include <linux/mii.h> | 43 | #include <linux/mii.h> |
43 | 44 | ||
44 | #include <asm/irq.h> | 45 | #include <asm/irq.h> |
@@ -1574,13 +1575,13 @@ static void sky2_tx_complete(struct sky2_port *sky2, u16 done) | |||
1574 | if (unlikely(netif_msg_tx_done(sky2))) | 1575 | if (unlikely(netif_msg_tx_done(sky2))) |
1575 | printk(KERN_DEBUG "%s: tx done %u\n", | 1576 | printk(KERN_DEBUG "%s: tx done %u\n", |
1576 | dev->name, idx); | 1577 | dev->name, idx); |
1578 | |||
1577 | sky2->net_stats.tx_packets++; | 1579 | sky2->net_stats.tx_packets++; |
1578 | sky2->net_stats.tx_bytes += re->skb->len; | 1580 | sky2->net_stats.tx_bytes += re->skb->len; |
1579 | 1581 | ||
1580 | dev_kfree_skb_any(re->skb); | 1582 | dev_kfree_skb_any(re->skb); |
1583 | sky2->tx_next = RING_NEXT(idx, TX_RING_SIZE); | ||
1581 | } | 1584 | } |
1582 | |||
1583 | le->opcode = 0; /* paranoia */ | ||
1584 | } | 1585 | } |
1585 | 1586 | ||
1586 | sky2->tx_cons = idx; | 1587 | sky2->tx_cons = idx; |
@@ -3470,6 +3471,195 @@ static const struct ethtool_ops sky2_ethtool_ops = { | |||
3470 | .get_perm_addr = ethtool_op_get_perm_addr, | 3471 | .get_perm_addr = ethtool_op_get_perm_addr, |
3471 | }; | 3472 | }; |
3472 | 3473 | ||
3474 | #ifdef CONFIG_SKY2_DEBUG | ||
3475 | |||
3476 | static struct dentry *sky2_debug; | ||
3477 | |||
3478 | static int sky2_debug_show(struct seq_file *seq, void *v) | ||
3479 | { | ||
3480 | struct net_device *dev = seq->private; | ||
3481 | const struct sky2_port *sky2 = netdev_priv(dev); | ||
3482 | const struct sky2_hw *hw = sky2->hw; | ||
3483 | unsigned port = sky2->port; | ||
3484 | unsigned idx, last; | ||
3485 | int sop; | ||
3486 | |||
3487 | if (!netif_running(dev)) | ||
3488 | return -ENETDOWN; | ||
3489 | |||
3490 | seq_printf(seq, "IRQ src=%x mask=%x control=%x\n", | ||
3491 | sky2_read32(hw, B0_ISRC), | ||
3492 | sky2_read32(hw, B0_IMSK), | ||
3493 | sky2_read32(hw, B0_Y2_SP_ICR)); | ||
3494 | |||
3495 | netif_poll_disable(hw->dev[0]); | ||
3496 | last = sky2_read16(hw, STAT_PUT_IDX); | ||
3497 | |||
3498 | if (hw->st_idx == last) | ||
3499 | seq_puts(seq, "Status ring (empty)\n"); | ||
3500 | else { | ||
3501 | seq_puts(seq, "Status ring\n"); | ||
3502 | for (idx = hw->st_idx; idx != last && idx < STATUS_RING_SIZE; | ||
3503 | idx = RING_NEXT(idx, STATUS_RING_SIZE)) { | ||
3504 | const struct sky2_status_le *le = hw->st_le + idx; | ||
3505 | seq_printf(seq, "[%d] %#x %d %#x\n", | ||
3506 | idx, le->opcode, le->length, le->status); | ||
3507 | } | ||
3508 | seq_puts(seq, "\n"); | ||
3509 | } | ||
3510 | |||
3511 | seq_printf(seq, "Tx ring pending=%u...%u report=%d done=%d\n", | ||
3512 | sky2->tx_cons, sky2->tx_prod, | ||
3513 | sky2_read16(hw, port == 0 ? STAT_TXA1_RIDX : STAT_TXA2_RIDX), | ||
3514 | sky2_read16(hw, Q_ADDR(txqaddr[port], Q_DONE))); | ||
3515 | |||
3516 | /* Dump contents of tx ring */ | ||
3517 | sop = 1; | ||
3518 | for (idx = sky2->tx_next; idx != sky2->tx_prod && idx < TX_RING_SIZE; | ||
3519 | idx = RING_NEXT(idx, TX_RING_SIZE)) { | ||
3520 | const struct sky2_tx_le *le = sky2->tx_le + idx; | ||
3521 | u32 a = le32_to_cpu(le->addr); | ||
3522 | |||
3523 | if (sop) | ||
3524 | seq_printf(seq, "%u:", idx); | ||
3525 | sop = 0; | ||
3526 | |||
3527 | switch(le->opcode & ~HW_OWNER) { | ||
3528 | case OP_ADDR64: | ||
3529 | seq_printf(seq, " %#x:", a); | ||
3530 | break; | ||
3531 | case OP_LRGLEN: | ||
3532 | seq_printf(seq, " mtu=%d", a); | ||
3533 | break; | ||
3534 | case OP_VLAN: | ||
3535 | seq_printf(seq, " vlan=%d", be16_to_cpu(le->length)); | ||
3536 | break; | ||
3537 | case OP_TCPLISW: | ||
3538 | seq_printf(seq, " csum=%#x", a); | ||
3539 | break; | ||
3540 | case OP_LARGESEND: | ||
3541 | seq_printf(seq, " tso=%#x(%d)", a, le16_to_cpu(le->length)); | ||
3542 | break; | ||
3543 | case OP_PACKET: | ||
3544 | seq_printf(seq, " %#x(%d)", a, le16_to_cpu(le->length)); | ||
3545 | break; | ||
3546 | case OP_BUFFER: | ||
3547 | seq_printf(seq, " frag=%#x(%d)", a, le16_to_cpu(le->length)); | ||
3548 | break; | ||
3549 | default: | ||
3550 | seq_printf(seq, " op=%#x,%#x(%d)", le->opcode, | ||
3551 | a, le16_to_cpu(le->length)); | ||
3552 | } | ||
3553 | |||
3554 | if (le->ctrl & EOP) { | ||
3555 | seq_putc(seq, '\n'); | ||
3556 | sop = 1; | ||
3557 | } | ||
3558 | } | ||
3559 | |||
3560 | seq_printf(seq, "\nRx ring hw get=%d put=%d last=%d\n", | ||
3561 | sky2_read16(hw, Y2_QADDR(rxqaddr[port], PREF_UNIT_GET_IDX)), | ||
3562 | last = sky2_read16(hw, Y2_QADDR(rxqaddr[port], PREF_UNIT_PUT_IDX)), | ||
3563 | sky2_read16(hw, Y2_QADDR(rxqaddr[port], PREF_UNIT_LAST_IDX))); | ||
3564 | |||
3565 | netif_poll_enable(hw->dev[0]); | ||
3566 | return 0; | ||
3567 | } | ||
3568 | |||
3569 | static int sky2_debug_open(struct inode *inode, struct file *file) | ||
3570 | { | ||
3571 | return single_open(file, sky2_debug_show, inode->i_private); | ||
3572 | } | ||
3573 | |||
3574 | static const struct file_operations sky2_debug_fops = { | ||
3575 | .owner = THIS_MODULE, | ||
3576 | .open = sky2_debug_open, | ||
3577 | .read = seq_read, | ||
3578 | .llseek = seq_lseek, | ||
3579 | .release = single_release, | ||
3580 | }; | ||
3581 | |||
3582 | /* | ||
3583 | * Use network device events to create/remove/rename | ||
3584 | * debugfs file entries | ||
3585 | */ | ||
3586 | static int sky2_device_event(struct notifier_block *unused, | ||
3587 | unsigned long event, void *ptr) | ||
3588 | { | ||
3589 | struct net_device *dev = ptr; | ||
3590 | |||
3591 | if (dev->open == sky2_up) { | ||
3592 | struct sky2_port *sky2 = netdev_priv(dev); | ||
3593 | |||
3594 | switch(event) { | ||
3595 | case NETDEV_CHANGENAME: | ||
3596 | if (!netif_running(dev)) | ||
3597 | break; | ||
3598 | /* fallthrough */ | ||
3599 | case NETDEV_DOWN: | ||
3600 | case NETDEV_GOING_DOWN: | ||
3601 | if (sky2->debugfs) { | ||
3602 | printk(KERN_DEBUG PFX "%s: remove debugfs\n", | ||
3603 | dev->name); | ||
3604 | debugfs_remove(sky2->debugfs); | ||
3605 | sky2->debugfs = NULL; | ||
3606 | } | ||
3607 | |||
3608 | if (event != NETDEV_CHANGENAME) | ||
3609 | break; | ||
3610 | /* fallthrough for changename */ | ||
3611 | case NETDEV_UP: | ||
3612 | if (sky2_debug) { | ||
3613 | struct dentry *d; | ||
3614 | d = debugfs_create_file(dev->name, S_IRUGO, | ||
3615 | sky2_debug, dev, | ||
3616 | &sky2_debug_fops); | ||
3617 | if (d == NULL || IS_ERR(d)) | ||
3618 | printk(KERN_INFO PFX | ||
3619 | "%s: debugfs create failed\n", | ||
3620 | dev->name); | ||
3621 | else | ||
3622 | sky2->debugfs = d; | ||
3623 | } | ||
3624 | break; | ||
3625 | } | ||
3626 | } | ||
3627 | |||
3628 | return NOTIFY_DONE; | ||
3629 | } | ||
3630 | |||
3631 | static struct notifier_block sky2_notifier = { | ||
3632 | .notifier_call = sky2_device_event, | ||
3633 | }; | ||
3634 | |||
3635 | |||
3636 | static __init void sky2_debug_init(void) | ||
3637 | { | ||
3638 | struct dentry *ent; | ||
3639 | |||
3640 | ent = debugfs_create_dir("sky2", NULL); | ||
3641 | if (!ent || IS_ERR(ent)) | ||
3642 | return; | ||
3643 | |||
3644 | sky2_debug = ent; | ||
3645 | register_netdevice_notifier(&sky2_notifier); | ||
3646 | } | ||
3647 | |||
3648 | static __exit void sky2_debug_cleanup(void) | ||
3649 | { | ||
3650 | if (sky2_debug) { | ||
3651 | unregister_netdevice_notifier(&sky2_notifier); | ||
3652 | debugfs_remove(sky2_debug); | ||
3653 | sky2_debug = NULL; | ||
3654 | } | ||
3655 | } | ||
3656 | |||
3657 | #else | ||
3658 | #define sky2_debug_init() | ||
3659 | #define sky2_debug_cleanup() | ||
3660 | #endif | ||
3661 | |||
3662 | |||
3473 | /* Initialize network device */ | 3663 | /* Initialize network device */ |
3474 | static __devinit struct net_device *sky2_init_netdev(struct sky2_hw *hw, | 3664 | static __devinit struct net_device *sky2_init_netdev(struct sky2_hw *hw, |
3475 | unsigned port, | 3665 | unsigned port, |
@@ -3960,12 +4150,14 @@ static struct pci_driver sky2_driver = { | |||
3960 | 4150 | ||
3961 | static int __init sky2_init_module(void) | 4151 | static int __init sky2_init_module(void) |
3962 | { | 4152 | { |
4153 | sky2_debug_init(); | ||
3963 | return pci_register_driver(&sky2_driver); | 4154 | return pci_register_driver(&sky2_driver); |
3964 | } | 4155 | } |
3965 | 4156 | ||
3966 | static void __exit sky2_cleanup_module(void) | 4157 | static void __exit sky2_cleanup_module(void) |
3967 | { | 4158 | { |
3968 | pci_unregister_driver(&sky2_driver); | 4159 | pci_unregister_driver(&sky2_driver); |
4160 | sky2_debug_cleanup(); | ||
3969 | } | 4161 | } |
3970 | 4162 | ||
3971 | module_init(sky2_init_module); | 4163 | module_init(sky2_init_module); |
diff --git a/drivers/net/sky2.h b/drivers/net/sky2.h index 8df4643493d1..dce4d276d443 100644 --- a/drivers/net/sky2.h +++ b/drivers/net/sky2.h | |||
@@ -1998,6 +1998,7 @@ struct sky2_port { | |||
1998 | struct sky2_tx_le *tx_le; | 1998 | struct sky2_tx_le *tx_le; |
1999 | u16 tx_cons; /* next le to check */ | 1999 | u16 tx_cons; /* next le to check */ |
2000 | u16 tx_prod; /* next le to use */ | 2000 | u16 tx_prod; /* next le to use */ |
2001 | u16 tx_next; /* debug only */ | ||
2001 | u32 tx_addr64; | 2002 | u32 tx_addr64; |
2002 | u16 tx_pending; | 2003 | u16 tx_pending; |
2003 | u16 tx_last_mss; | 2004 | u16 tx_last_mss; |
@@ -2028,6 +2029,9 @@ struct sky2_port { | |||
2028 | enum flow_control flow_mode; | 2029 | enum flow_control flow_mode; |
2029 | enum flow_control flow_status; | 2030 | enum flow_control flow_status; |
2030 | 2031 | ||
2032 | #ifdef CONFIG_SKY2_DEBUG | ||
2033 | struct dentry *debugfs; | ||
2034 | #endif | ||
2031 | struct net_device_stats net_stats; | 2035 | struct net_device_stats net_stats; |
2032 | 2036 | ||
2033 | }; | 2037 | }; |