qlge: Add ethtool self-test.
Signed-off-by: Ron Mercer <ron.mercer@qlogic.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
536b2e92f1
commit
9dfbbaa6b0
|
@ -1581,6 +1581,8 @@ enum {
|
|||
QL_ALLMULTI = 6,
|
||||
QL_PORT_CFG = 7,
|
||||
QL_CAM_RT_SET = 8,
|
||||
QL_SELFTEST = 9,
|
||||
QL_LB_LINK_UP = 10,
|
||||
};
|
||||
|
||||
/* link_status bit definitions */
|
||||
|
@ -1717,6 +1719,7 @@ struct ql_adapter {
|
|||
struct completion ide_completion;
|
||||
struct nic_operations *nic_ops;
|
||||
u16 device_id;
|
||||
atomic_t lb_count;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -1808,6 +1811,9 @@ int ql_mb_set_port_cfg(struct ql_adapter *qdev);
|
|||
int ql_wait_fifo_empty(struct ql_adapter *qdev);
|
||||
void ql_gen_reg_dump(struct ql_adapter *qdev,
|
||||
struct ql_reg_dump *mpi_coredump);
|
||||
netdev_tx_t ql_lb_send(struct sk_buff *skb, struct net_device *ndev);
|
||||
void ql_check_lb_frame(struct ql_adapter *, struct sk_buff *);
|
||||
int ql_clean_lb_rx_ring(struct rx_ring *rx_ring, int budget);
|
||||
|
||||
#if 1
|
||||
#define QL_ALL_DUMP
|
||||
|
|
|
@ -36,6 +36,11 @@
|
|||
|
||||
#include "qlge.h"
|
||||
|
||||
static const char ql_gstrings_test[][ETH_GSTRING_LEN] = {
|
||||
"Loopback test (offline)"
|
||||
};
|
||||
#define QLGE_TEST_LEN (sizeof(ql_gstrings_test) / ETH_GSTRING_LEN)
|
||||
|
||||
static int ql_update_ring_coalescing(struct ql_adapter *qdev)
|
||||
{
|
||||
int i, status = 0;
|
||||
|
@ -251,6 +256,8 @@ static void ql_get_strings(struct net_device *dev, u32 stringset, u8 *buf)
|
|||
static int ql_get_sset_count(struct net_device *dev, int sset)
|
||||
{
|
||||
switch (sset) {
|
||||
case ETH_SS_TEST:
|
||||
return QLGE_TEST_LEN;
|
||||
case ETH_SS_STATS:
|
||||
return ARRAY_SIZE(ql_stats_str_arr);
|
||||
default:
|
||||
|
@ -429,6 +436,110 @@ static int ql_phys_id(struct net_device *ndev, u32 data)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ql_start_loopback(struct ql_adapter *qdev)
|
||||
{
|
||||
if (netif_carrier_ok(qdev->ndev)) {
|
||||
set_bit(QL_LB_LINK_UP, &qdev->flags);
|
||||
netif_carrier_off(qdev->ndev);
|
||||
} else
|
||||
clear_bit(QL_LB_LINK_UP, &qdev->flags);
|
||||
qdev->link_config |= CFG_LOOPBACK_PCS;
|
||||
return ql_mb_set_port_cfg(qdev);
|
||||
}
|
||||
|
||||
static void ql_stop_loopback(struct ql_adapter *qdev)
|
||||
{
|
||||
qdev->link_config &= ~CFG_LOOPBACK_PCS;
|
||||
ql_mb_set_port_cfg(qdev);
|
||||
if (test_bit(QL_LB_LINK_UP, &qdev->flags)) {
|
||||
netif_carrier_on(qdev->ndev);
|
||||
clear_bit(QL_LB_LINK_UP, &qdev->flags);
|
||||
}
|
||||
}
|
||||
|
||||
static void ql_create_lb_frame(struct sk_buff *skb,
|
||||
unsigned int frame_size)
|
||||
{
|
||||
memset(skb->data, 0xFF, frame_size);
|
||||
frame_size &= ~1;
|
||||
memset(&skb->data[frame_size / 2], 0xAA, frame_size / 2 - 1);
|
||||
memset(&skb->data[frame_size / 2 + 10], 0xBE, 1);
|
||||
memset(&skb->data[frame_size / 2 + 12], 0xAF, 1);
|
||||
}
|
||||
|
||||
void ql_check_lb_frame(struct ql_adapter *qdev,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
unsigned int frame_size = skb->len;
|
||||
|
||||
if ((*(skb->data + 3) == 0xFF) &&
|
||||
(*(skb->data + frame_size / 2 + 10) == 0xBE) &&
|
||||
(*(skb->data + frame_size / 2 + 12) == 0xAF)) {
|
||||
atomic_dec(&qdev->lb_count);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static int ql_run_loopback_test(struct ql_adapter *qdev)
|
||||
{
|
||||
int i;
|
||||
netdev_tx_t rc;
|
||||
struct sk_buff *skb;
|
||||
unsigned int size = SMALL_BUF_MAP_SIZE;
|
||||
|
||||
for (i = 0; i < 64; i++) {
|
||||
skb = netdev_alloc_skb(qdev->ndev, size);
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
skb->queue_mapping = 0;
|
||||
skb_put(skb, size);
|
||||
ql_create_lb_frame(skb, size);
|
||||
rc = ql_lb_send(skb, qdev->ndev);
|
||||
if (rc != NETDEV_TX_OK)
|
||||
return -EPIPE;
|
||||
atomic_inc(&qdev->lb_count);
|
||||
}
|
||||
|
||||
ql_clean_lb_rx_ring(&qdev->rx_ring[0], 128);
|
||||
return atomic_read(&qdev->lb_count) ? -EIO : 0;
|
||||
}
|
||||
|
||||
static int ql_loopback_test(struct ql_adapter *qdev, u64 *data)
|
||||
{
|
||||
*data = ql_start_loopback(qdev);
|
||||
if (*data)
|
||||
goto out;
|
||||
*data = ql_run_loopback_test(qdev);
|
||||
out:
|
||||
ql_stop_loopback(qdev);
|
||||
return *data;
|
||||
}
|
||||
|
||||
static void ql_self_test(struct net_device *ndev,
|
||||
struct ethtool_test *eth_test, u64 *data)
|
||||
{
|
||||
struct ql_adapter *qdev = netdev_priv(ndev);
|
||||
|
||||
if (netif_running(ndev)) {
|
||||
set_bit(QL_SELFTEST, &qdev->flags);
|
||||
if (eth_test->flags == ETH_TEST_FL_OFFLINE) {
|
||||
/* Offline tests */
|
||||
if (ql_loopback_test(qdev, &data[0]))
|
||||
eth_test->flags |= ETH_TEST_FL_FAILED;
|
||||
|
||||
} else {
|
||||
/* Online tests */
|
||||
data[0] = 0;
|
||||
}
|
||||
clear_bit(QL_SELFTEST, &qdev->flags);
|
||||
} else {
|
||||
QPRINTK(qdev, DRV, ERR,
|
||||
"%s: is down, Loopback test will fail.\n", ndev->name);
|
||||
eth_test->flags |= ETH_TEST_FL_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
static int ql_get_regs_len(struct net_device *ndev)
|
||||
{
|
||||
return sizeof(struct ql_reg_dump);
|
||||
|
@ -575,6 +686,7 @@ const struct ethtool_ops qlge_ethtool_ops = {
|
|||
.set_msglevel = ql_set_msglevel,
|
||||
.get_link = ethtool_op_get_link,
|
||||
.phys_id = ql_phys_id,
|
||||
.self_test = ql_self_test,
|
||||
.get_pauseparam = ql_get_pauseparam,
|
||||
.set_pauseparam = ql_set_pauseparam,
|
||||
.get_rx_csum = ql_get_rx_csum,
|
||||
|
|
|
@ -1680,6 +1680,13 @@ static void ql_process_mac_rx_intr(struct ql_adapter *qdev,
|
|||
return;
|
||||
}
|
||||
|
||||
/* loopback self test for ethtool */
|
||||
if (test_bit(QL_SELFTEST, &qdev->flags)) {
|
||||
ql_check_lb_frame(qdev, skb);
|
||||
dev_kfree_skb_any(skb);
|
||||
return;
|
||||
}
|
||||
|
||||
prefetch(skb->data);
|
||||
skb->dev = ndev;
|
||||
if (ib_mac_rsp->flags1 & IB_MAC_IOCB_RSP_M_MASK) {
|
||||
|
@ -2248,6 +2255,7 @@ static netdev_tx_t qlge_send(struct sk_buff *skb, struct net_device *ndev)
|
|||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
|
||||
static void ql_free_shadow_space(struct ql_adapter *qdev)
|
||||
{
|
||||
if (qdev->rx_ring_shadow_reg_area) {
|
||||
|
@ -4174,7 +4182,6 @@ err_out:
|
|||
return err;
|
||||
}
|
||||
|
||||
|
||||
static const struct net_device_ops qlge_netdev_ops = {
|
||||
.ndo_open = qlge_open,
|
||||
.ndo_stop = qlge_close,
|
||||
|
@ -4243,10 +4250,21 @@ static int __devinit qlge_probe(struct pci_dev *pdev,
|
|||
}
|
||||
ql_link_off(qdev);
|
||||
ql_display_dev_info(ndev);
|
||||
atomic_set(&qdev->lb_count, 0);
|
||||
cards_found++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
netdev_tx_t ql_lb_send(struct sk_buff *skb, struct net_device *ndev)
|
||||
{
|
||||
return qlge_send(skb, ndev);
|
||||
}
|
||||
|
||||
int ql_clean_lb_rx_ring(struct rx_ring *rx_ring, int budget)
|
||||
{
|
||||
return ql_clean_inbound_rx_ring(rx_ring, budget);
|
||||
}
|
||||
|
||||
static void __devexit qlge_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct net_device *ndev = pci_get_drvdata(pdev);
|
||||
|
|
Loading…
Reference in a new issue