diff options
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/fec.c | 125 | ||||
-rw-r--r-- | drivers/net/fec.h | 4 | ||||
-rw-r--r-- | drivers/net/fec_mpc52xx.c | 97 | ||||
-rw-r--r-- | drivers/net/fec_mpc52xx.h | 19 | ||||
-rw-r--r-- | drivers/net/mlx4/mr.c | 2 | ||||
-rw-r--r-- | drivers/net/usb/asix.c | 4 | ||||
-rw-r--r-- | drivers/net/virtio_net.c | 96 |
7 files changed, 206 insertions, 141 deletions
diff --git a/drivers/net/fec.c b/drivers/net/fec.c index d7a3ea88edd..32a4f17d35f 100644 --- a/drivers/net/fec.c +++ b/drivers/net/fec.c @@ -67,6 +67,10 @@ #define FEC_MAX_PORTS 1 #endif +#if defined(CONFIG_FADS) || defined(CONFIG_RPXCLASSIC) || defined(CONFIG_M5272) +#define HAVE_mii_link_interrupt +#endif + /* * Define the fixed address of the FEC hardware. */ @@ -205,7 +209,10 @@ struct fec_enet_private { cbd_t *cur_rx, *cur_tx; /* The next free ring entry */ cbd_t *dirty_tx; /* The ring entries to be free()ed. */ uint tx_full; - spinlock_t lock; + /* hold while accessing the HW like ringbuffer for tx/rx but not MAC */ + spinlock_t hw_lock; + /* hold while accessing the mii_list_t() elements */ + spinlock_t mii_lock; uint phy_id; uint phy_id_done; @@ -309,6 +316,7 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev) volatile fec_t *fecp; volatile cbd_t *bdp; unsigned short status; + unsigned long flags; fep = netdev_priv(dev); fecp = (volatile fec_t*)dev->base_addr; @@ -318,6 +326,7 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev) return 1; } + spin_lock_irqsave(&fep->hw_lock, flags); /* Fill in a Tx ring entry */ bdp = fep->cur_tx; @@ -328,6 +337,7 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev) * This should not happen, since dev->tbusy should be set. */ printk("%s: tx queue full!.\n", dev->name); + spin_unlock_irqrestore(&fep->hw_lock, flags); return 1; } #endif @@ -366,8 +376,6 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev) flush_dcache_range((unsigned long)skb->data, (unsigned long)skb->data + skb->len); - spin_lock_irq(&fep->lock); - /* Send it on its way. Tell FEC it's ready, interrupt when done, * it's the last BD of the frame, and to put the CRC on the end. */ @@ -396,7 +404,7 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev) fep->cur_tx = (cbd_t *)bdp; - spin_unlock_irq(&fep->lock); + spin_unlock_irqrestore(&fep->hw_lock, flags); return 0; } @@ -454,19 +462,20 @@ fec_enet_interrupt(int irq, void * dev_id) struct net_device *dev = dev_id; volatile fec_t *fecp; uint int_events; - int handled = 0; + irqreturn_t ret = IRQ_NONE; fecp = (volatile fec_t*)dev->base_addr; /* Get the interrupt events that caused us to be here. */ - while ((int_events = fecp->fec_ievent) != 0) { + do { + int_events = fecp->fec_ievent; fecp->fec_ievent = int_events; /* Handle receive event in its own function. */ if (int_events & FEC_ENET_RXF) { - handled = 1; + ret = IRQ_HANDLED; fec_enet_rx(dev); } @@ -475,17 +484,18 @@ fec_enet_interrupt(int irq, void * dev_id) them as part of the transmit process. */ if (int_events & FEC_ENET_TXF) { - handled = 1; + ret = IRQ_HANDLED; fec_enet_tx(dev); } if (int_events & FEC_ENET_MII) { - handled = 1; + ret = IRQ_HANDLED; fec_enet_mii(dev); } - } - return IRQ_RETVAL(handled); + } while (int_events); + + return ret; } @@ -498,7 +508,7 @@ fec_enet_tx(struct net_device *dev) struct sk_buff *skb; fep = netdev_priv(dev); - spin_lock(&fep->lock); + spin_lock_irq(&fep->hw_lock); bdp = fep->dirty_tx; while (((status = bdp->cbd_sc) & BD_ENET_TX_READY) == 0) { @@ -557,7 +567,7 @@ fec_enet_tx(struct net_device *dev) } } fep->dirty_tx = (cbd_t *)bdp; - spin_unlock(&fep->lock); + spin_unlock_irq(&fep->hw_lock); } @@ -584,6 +594,8 @@ fec_enet_rx(struct net_device *dev) fep = netdev_priv(dev); fecp = (volatile fec_t*)dev->base_addr; + spin_lock_irq(&fep->hw_lock); + /* First, grab all of the stats for the incoming packet. * These get messed up if we get called due to a busy condition. */ @@ -689,6 +701,8 @@ while (!((status = bdp->cbd_sc) & BD_ENET_RX_EMPTY)) { */ fecp->fec_r_des_active = 0; #endif + + spin_unlock_irq(&fep->hw_lock); } @@ -702,11 +716,11 @@ fec_enet_mii(struct net_device *dev) uint mii_reg; fep = netdev_priv(dev); + spin_lock_irq(&fep->mii_lock); + ep = fep->hwp; mii_reg = ep->fec_mii_data; - spin_lock(&fep->lock); - if ((mip = mii_head) == NULL) { printk("MII and no head!\n"); goto unlock; @@ -723,7 +737,7 @@ fec_enet_mii(struct net_device *dev) ep->fec_mii_data = mip->mii_regval; unlock: - spin_unlock(&fep->lock); + spin_unlock_irq(&fep->mii_lock); } static int @@ -737,12 +751,11 @@ mii_queue(struct net_device *dev, int regval, void (*func)(uint, struct net_devi /* Add PHY address to register command. */ fep = netdev_priv(dev); - regval |= fep->phy_addr << 23; + spin_lock_irqsave(&fep->mii_lock, flags); + regval |= fep->phy_addr << 23; retval = 0; - spin_lock_irqsave(&fep->lock,flags); - if ((mip = mii_free) != NULL) { mii_free = mip->mii_next; mip->mii_regval = regval; @@ -759,9 +772,8 @@ mii_queue(struct net_device *dev, int regval, void (*func)(uint, struct net_devi retval = 1; } - spin_unlock_irqrestore(&fep->lock,flags); - - return(retval); + spin_unlock_irqrestore(&fep->mii_lock, flags); + return retval; } static void mii_do_cmd(struct net_device *dev, const phy_cmd_t *c) @@ -1222,7 +1234,7 @@ static phy_info_t const * const phy_info[] = { }; /* ------------------------------------------------------------------------- */ -#if !defined(CONFIG_M532x) +#ifdef HAVE_mii_link_interrupt #ifdef CONFIG_RPXCLASSIC static void mii_link_interrupt(void *dev_id); @@ -1362,18 +1374,8 @@ static void __inline__ fec_request_intrs(struct net_device *dev) unsigned short irq; } *idp, id[] = { { "fec(TXF)", 23 }, - { "fec(TXB)", 24 }, - { "fec(TXFIFO)", 25 }, - { "fec(TXCR)", 26 }, { "fec(RXF)", 27 }, - { "fec(RXB)", 28 }, { "fec(MII)", 29 }, - { "fec(LC)", 30 }, - { "fec(HBERR)", 31 }, - { "fec(GRA)", 32 }, - { "fec(EBERR)", 33 }, - { "fec(BABT)", 34 }, - { "fec(BABR)", 35 }, { NULL }, }; @@ -1533,18 +1535,8 @@ static void __inline__ fec_request_intrs(struct net_device *dev) unsigned short irq; } *idp, id[] = { { "fec(TXF)", 23 }, - { "fec(TXB)", 24 }, - { "fec(TXFIFO)", 25 }, - { "fec(TXCR)", 26 }, { "fec(RXF)", 27 }, - { "fec(RXB)", 28 }, { "fec(MII)", 29 }, - { "fec(LC)", 30 }, - { "fec(HBERR)", 31 }, - { "fec(GRA)", 32 }, - { "fec(EBERR)", 33 }, - { "fec(BABT)", 34 }, - { "fec(BABR)", 35 }, { NULL }, }; @@ -1660,18 +1652,8 @@ static void __inline__ fec_request_intrs(struct net_device *dev) unsigned short irq; } *idp, id[] = { { "fec(TXF)", 36 }, - { "fec(TXB)", 37 }, - { "fec(TXFIFO)", 38 }, - { "fec(TXCR)", 39 }, { "fec(RXF)", 40 }, - { "fec(RXB)", 41 }, { "fec(MII)", 42 }, - { "fec(LC)", 43 }, - { "fec(HBERR)", 44 }, - { "fec(GRA)", 45 }, - { "fec(EBERR)", 46 }, - { "fec(BABT)", 47 }, - { "fec(BABR)", 48 }, { NULL }, }; @@ -2126,6 +2108,7 @@ mii_discover_phy(uint mii_reg, struct net_device *dev) /* This interrupt occurs when the PHY detects a link change. */ +#ifdef HAVE_mii_link_interrupt #ifdef CONFIG_RPXCLASSIC static void mii_link_interrupt(void *dev_id) @@ -2148,6 +2131,7 @@ mii_link_interrupt(int irq, void * dev_id) return IRQ_HANDLED; } +#endif static int fec_enet_open(struct net_device *dev) @@ -2243,13 +2227,13 @@ static void set_multicast_list(struct net_device *dev) /* Catch all multicast addresses, so set the * filter to all 1's. */ - ep->fec_hash_table_high = 0xffffffff; - ep->fec_hash_table_low = 0xffffffff; + ep->fec_grp_hash_table_high = 0xffffffff; + ep->fec_grp_hash_table_low = 0xffffffff; } else { /* Clear filter and add the addresses in hash register. */ - ep->fec_hash_table_high = 0; - ep->fec_hash_table_low = 0; + ep->fec_grp_hash_table_high = 0; + ep->fec_grp_hash_table_low = 0; dmi = dev->mc_list; @@ -2280,9 +2264,9 @@ static void set_multicast_list(struct net_device *dev) hash = (crc >> (32 - HASH_BITS)) & 0x3f; if (hash > 31) - ep->fec_hash_table_high |= 1 << (hash - 32); + ep->fec_grp_hash_table_high |= 1 << (hash - 32); else - ep->fec_hash_table_low |= 1 << hash; + ep->fec_grp_hash_table_low |= 1 << hash; } } } @@ -2332,6 +2316,9 @@ int __init fec_enet_init(struct net_device *dev) return -ENOMEM; } + spin_lock_init(&fep->hw_lock); + spin_lock_init(&fep->mii_lock); + /* Create an Ethernet device instance. */ fecp = (volatile fec_t *) fec_hw[index]; @@ -2430,11 +2417,15 @@ int __init fec_enet_init(struct net_device *dev) */ fec_request_intrs(dev); - fecp->fec_hash_table_high = 0; - fecp->fec_hash_table_low = 0; + fecp->fec_grp_hash_table_high = 0; + fecp->fec_grp_hash_table_low = 0; fecp->fec_r_buff_size = PKT_MAXBLR_SIZE; fecp->fec_ecntrl = 2; fecp->fec_r_des_active = 0; +#ifndef CONFIG_M5272 + fecp->fec_hash_table_high = 0; + fecp->fec_hash_table_low = 0; +#endif dev->base_addr = (unsigned long)fecp; @@ -2455,8 +2446,7 @@ int __init fec_enet_init(struct net_device *dev) /* Clear and enable interrupts */ fecp->fec_ievent = 0xffc00000; - fecp->fec_imask = (FEC_ENET_TXF | FEC_ENET_TXB | - FEC_ENET_RXF | FEC_ENET_RXB | FEC_ENET_MII); + fecp->fec_imask = (FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII); /* Queue up command to detect the PHY and initialize the * remainder of the interface. @@ -2500,8 +2490,8 @@ fec_restart(struct net_device *dev, int duplex) /* Reset all multicast. */ - fecp->fec_hash_table_high = 0; - fecp->fec_hash_table_low = 0; + fecp->fec_grp_hash_table_high = 0; + fecp->fec_grp_hash_table_low = 0; /* Set maximum receive buffer size. */ @@ -2583,8 +2573,7 @@ fec_restart(struct net_device *dev, int duplex) /* Enable interrupts we wish to service. */ - fecp->fec_imask = (FEC_ENET_TXF | FEC_ENET_TXB | - FEC_ENET_RXF | FEC_ENET_RXB | FEC_ENET_MII); + fecp->fec_imask = (FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII); } static void @@ -2624,7 +2613,7 @@ fec_stop(struct net_device *dev) static int __init fec_enet_module_init(void) { struct net_device *dev; - int i, j, err; + int i, err; DECLARE_MAC_BUF(mac); printk("FEC ENET Version 0.2\n"); diff --git a/drivers/net/fec.h b/drivers/net/fec.h index 1d421606984..292719dacef 100644 --- a/drivers/net/fec.h +++ b/drivers/net/fec.h @@ -88,8 +88,8 @@ typedef struct fec { unsigned long fec_reserved7[158]; unsigned long fec_addr_low; /* Low 32bits MAC address */ unsigned long fec_addr_high; /* High 16bits MAC address */ - unsigned long fec_hash_table_high; /* High 32bits hash table */ - unsigned long fec_hash_table_low; /* Low 32bits hash table */ + unsigned long fec_grp_hash_table_high;/* High 32bits hash table */ + unsigned long fec_grp_hash_table_low; /* Low 32bits hash table */ unsigned long fec_r_des_start; /* Receive descriptor ring */ unsigned long fec_x_des_start; /* Transmit descriptor ring */ unsigned long fec_r_buff_size; /* Maximum receive buff size */ diff --git a/drivers/net/fec_mpc52xx.c b/drivers/net/fec_mpc52xx.c index d21b7ab64bd..5f9c42e7a7f 100644 --- a/drivers/net/fec_mpc52xx.c +++ b/drivers/net/fec_mpc52xx.c @@ -43,6 +43,29 @@ #define DRIVER_NAME "mpc52xx-fec" +#define FEC5200_PHYADDR_NONE (-1) +#define FEC5200_PHYADDR_7WIRE (-2) + +/* Private driver data structure */ +struct mpc52xx_fec_priv { + int duplex; + int speed; + int r_irq; + int t_irq; + struct mpc52xx_fec __iomem *fec; + struct bcom_task *rx_dmatsk; + struct bcom_task *tx_dmatsk; + spinlock_t lock; + int msg_enable; + + /* MDIO link details */ + int phy_addr; + unsigned int phy_speed; + struct phy_device *phydev; + enum phy_state link; +}; + + static irqreturn_t mpc52xx_fec_interrupt(int, void *); static irqreturn_t mpc52xx_fec_rx_interrupt(int, void *); static irqreturn_t mpc52xx_fec_tx_interrupt(int, void *); @@ -223,7 +246,7 @@ static int mpc52xx_fec_phy_start(struct net_device *dev) struct mpc52xx_fec_priv *priv = netdev_priv(dev); int err; - if (!priv->has_phy) + if (priv->phy_addr < 0) return 0; err = mpc52xx_fec_init_phy(dev); @@ -243,7 +266,7 @@ static void mpc52xx_fec_phy_stop(struct net_device *dev) { struct mpc52xx_fec_priv *priv = netdev_priv(dev); - if (!priv->has_phy) + if (!priv->phydev) return; phy_disconnect(priv->phydev); @@ -255,7 +278,7 @@ static void mpc52xx_fec_phy_stop(struct net_device *dev) static int mpc52xx_fec_phy_mii_ioctl(struct mpc52xx_fec_priv *priv, struct mii_ioctl_data *mii_data, int cmd) { - if (!priv->has_phy) + if (!priv->phydev) return -ENOTSUPP; return phy_mii_ioctl(priv->phydev, mii_data, cmd); @@ -265,7 +288,7 @@ static void mpc52xx_fec_phy_hw_init(struct mpc52xx_fec_priv *priv) { struct mpc52xx_fec __iomem *fec = priv->fec; - if (!priv->has_phy) + if (priv->phydev) return; out_be32(&fec->mii_speed, priv->phy_speed); @@ -704,7 +727,7 @@ static void mpc52xx_fec_start(struct net_device *dev) rcntrl = FEC_RX_BUFFER_SIZE << 16; /* max frame length */ rcntrl |= FEC_RCNTRL_FCE; - if (priv->has_phy) + if (priv->phy_addr != FEC5200_PHYADDR_7WIRE) rcntrl |= FEC_RCNTRL_MII_MODE; if (priv->duplex == DUPLEX_FULL) @@ -864,7 +887,10 @@ mpc52xx_fec_probe(struct of_device *op, const struct of_device_id *match) struct net_device *ndev; struct mpc52xx_fec_priv *priv = NULL; struct resource mem; - const phandle *ph; + struct device_node *phy_node; + const phandle *phy_handle; + const u32 *prop; + int prop_size; phys_addr_t rx_fifo; phys_addr_t tx_fifo; @@ -948,26 +974,37 @@ mpc52xx_fec_probe(struct of_device *op, const struct of_device_id *match) mpc52xx_fec_get_paddr(ndev, ndev->dev_addr); priv->msg_enable = netif_msg_init(debug, MPC52xx_MESSAGES_DEFAULT); - priv->duplex = DUPLEX_FULL; - - /* is the phy present in device tree? */ - ph = of_get_property(op->node, "phy-handle", NULL); - if (ph) { - const unsigned int *prop; - struct device_node *phy_dn; - priv->has_phy = 1; - phy_dn = of_find_node_by_phandle(*ph); - prop = of_get_property(phy_dn, "reg", NULL); - priv->phy_addr = *prop; + /* + * Link mode configuration + */ - of_node_put(phy_dn); + /* Start with safe defaults for link connection */ + priv->phy_addr = FEC5200_PHYADDR_NONE; + priv->speed = 100; + priv->duplex = DUPLEX_HALF; + priv->phy_speed = ((mpc52xx_find_ipb_freq(op->node) >> 20) / 5) << 1; + + /* the 7-wire property means don't use MII mode */ + if (of_find_property(op->node, "fsl,7-wire-mode", NULL)) + priv->phy_addr = FEC5200_PHYADDR_7WIRE; + + /* The current speed preconfigures the speed of the MII link */ + prop = of_get_property(op->node, "current-speed", &prop_size); + if (prop && (prop_size >= sizeof(u32) * 2)) { + priv->speed = prop[0]; + priv->duplex = prop[1] ? DUPLEX_FULL : DUPLEX_HALF; + } - /* Phy speed */ - priv->phy_speed = ((mpc52xx_find_ipb_freq(op->node) >> 20) / 5) << 1; - } else { - dev_info(&ndev->dev, "can't find \"phy-handle\" in device" - " tree, using 7-wire mode\n"); + /* If there is a phy handle, setup link to that phy */ + phy_handle = of_get_property(op->node, "phy-handle", &prop_size); + if (phy_handle && (prop_size >= sizeof(phandle))) { + phy_node = of_find_node_by_phandle(*phy_handle); + prop = of_get_property(phy_node, "reg", &prop_size); + if (prop && (prop_size >= sizeof(u32))) + if ((*prop >= 0) && (*prop < PHY_MAX_ADDR)) + priv->phy_addr = *prop; + of_node_put(phy_node); } /* Hardware init */ @@ -982,6 +1019,20 @@ mpc52xx_fec_probe(struct of_device *op, const struct of_device_id *match) if (rv < 0) goto probe_error; + /* Now report the link setup */ + switch (priv->phy_addr) { + case FEC5200_PHYADDR_NONE: + dev_info(&ndev->dev, "Fixed speed MII link: %i%cD\n", + priv->speed, priv->duplex ? 'F' : 'H'); + break; + case FEC5200_PHYADDR_7WIRE: + dev_info(&ndev->dev, "using 7-wire PHY mode\n"); + break; + default: + dev_info(&ndev->dev, "Using PHY at MDIO address %i\n", + priv->phy_addr); + } + /* We're done ! */ dev_set_drvdata(&op->dev, ndev); diff --git a/drivers/net/fec_mpc52xx.h b/drivers/net/fec_mpc52xx.h index 8b1f75397b9..a227a525bdb 100644 --- a/drivers/net/fec_mpc52xx.h +++ b/drivers/net/fec_mpc52xx.h @@ -26,25 +26,6 @@ #define FEC_WATCHDOG_TIMEOUT ((400*HZ)/1000) -struct mpc52xx_fec_priv { - int duplex; - int r_irq; - int t_irq; - struct mpc52xx_fec __iomem *fec; - struct bcom_task *rx_dmatsk; - struct bcom_task *tx_dmatsk; - spinlock_t lock; - int msg_enable; - - int has_phy; - unsigned int phy_speed; - unsigned int phy_addr; - struct phy_device *phydev; - enum phy_state link; - int speed; -}; - - /* ======================================================================== */ /* Hardware register sets & bits */ /* ======================================================================== */ diff --git a/drivers/net/mlx4/mr.c b/drivers/net/mlx4/mr.c index cb46446b269..03a9abcce52 100644 --- a/drivers/net/mlx4/mr.c +++ b/drivers/net/mlx4/mr.c @@ -551,7 +551,7 @@ int mlx4_fmr_alloc(struct mlx4_dev *dev, u32 pd, u32 access, int max_pages, u64 mtt_seg; int err = -ENOMEM; - if (page_shift < 12 || page_shift >= 32) + if (page_shift < (ffs(dev->caps.page_size_cap) - 1) || page_shift >= 32) return -EINVAL; /* All MTTs must fit in the same page */ diff --git a/drivers/net/usb/asix.c b/drivers/net/usb/asix.c index 6f245cfb662..dc6f097062d 100644 --- a/drivers/net/usb/asix.c +++ b/drivers/net/usb/asix.c @@ -1381,6 +1381,10 @@ static const struct usb_device_id products [] = { USB_DEVICE (0x0411, 0x003d), .driver_info = (unsigned long) &ax8817x_info, }, { + // Buffalo LUA-U2-GT 10/100/1000 + USB_DEVICE (0x0411, 0x006e), + .driver_info = (unsigned long) &ax88178_info, +}, { // Sitecom LN-029 "USB 2.0 10/100 Ethernet adapter" USB_DEVICE (0x6189, 0x182d), .driver_info = (unsigned long) &ax8817x_info, diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 555b70c8b86..f926b5ab3d0 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -41,6 +41,9 @@ struct virtnet_info struct net_device *dev; struct napi_struct napi; + /* The skb we couldn't send because buffers were full. */ + struct sk_buff *last_xmit_skb; + /* Number of input buffers, and max we've ever had. */ unsigned int num, max; @@ -142,10 +145,10 @@ drop: static void try_fill_recv(struct virtnet_info *vi) { struct sk_buff *skb; - struct scatterlist sg[1+MAX_SKB_FRAGS]; + struct scatterlist sg[2+MAX_SKB_FRAGS]; int num, err; - sg_init_table(sg, 1+MAX_SKB_FRAGS); + sg_init_table(sg, 2+MAX_SKB_FRAGS); for (;;) { skb = netdev_alloc_skb(vi->dev, MAX_PACKET_LEN); if (unlikely(!skb)) @@ -221,23 +224,22 @@ static void free_old_xmit_skbs(struct virtnet_info *vi) while ((skb = vi->svq->vq_ops->get_buf(vi->svq, &len)) != NULL) { pr_debug("Sent skb %p\n", skb); __skb_unlink(skb, &vi->send); - vi->dev->stats.tx_bytes += len; + vi->dev->stats.tx_bytes += skb->len; vi->dev->stats.tx_packets++; kfree_skb(skb); } } -static int start_xmit(struct sk_buff *skb, struct net_device *dev) +static int xmit_skb(struct virtnet_info *vi, struct sk_buff *skb) { - struct virtnet_info *vi = netdev_priv(dev); - int num, err; - struct scatterlist sg[1+MAX_SKB_FRAGS]; + int num; + struct scatterlist sg[2+MAX_SKB_FRAGS]; struct virtio_net_hdr *hdr; const unsigned char *dest = ((struct ethhdr *)skb->data)->h_dest; - sg_init_table(sg, 1+MAX_SKB_FRAGS); + sg_init_table(sg, 2+MAX_SKB_FRAGS); - pr_debug("%s: xmit %p " MAC_FMT "\n", dev->name, skb, + pr_debug("%s: xmit %p " MAC_FMT "\n", vi->dev->name, skb, dest[0], dest[1], dest[2], dest[3], dest[4], dest[5]); @@ -272,30 +274,51 @@ static int start_xmit(struct sk_buff *skb, struct net_device *dev) vnet_hdr_to_sg(sg, skb); num = skb_to_sgvec(skb, sg+1, 0, skb->len) + 1; - __skb_queue_head(&vi->send, skb); + + return vi->svq->vq_ops->add_buf(vi->svq, sg, num, 0, skb); +} + +static int start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct virtnet_info *vi = netdev_priv(dev); again: /* Free up any pending old buffers before queueing new ones. */ free_old_xmit_skbs(vi); - err = vi->svq->vq_ops->add_buf(vi->svq, sg, num, 0, skb); - if (err) { - pr_debug("%s: virtio not prepared to send\n", dev->name); - netif_stop_queue(dev); - - /* Activate callback for using skbs: if this returns false it - * means some were used in the meantime. */ - if (unlikely(!vi->svq->vq_ops->enable_cb(vi->svq))) { - vi->svq->vq_ops->disable_cb(vi->svq); - netif_start_queue(dev); - goto again; + + /* If we has a buffer left over from last time, send it now. */ + if (vi->last_xmit_skb) { + if (xmit_skb(vi, vi->last_xmit_skb) != 0) { + /* Drop this skb: we only queue one. */ + vi->dev->stats.tx_dropped++; + kfree_skb(skb); + goto stop_queue; } - __skb_unlink(skb, &vi->send); + vi->last_xmit_skb = NULL; + } - return NETDEV_TX_BUSY; + /* Put new one in send queue and do transmit */ + __skb_queue_head(&vi->send, skb); + if (xmit_skb(vi, skb) != 0) { + vi->last_xmit_skb = skb; + goto stop_queue; } +done: vi->svq->vq_ops->kick(vi->svq); - - return 0; + return NETDEV_TX_OK; + +stop_queue: + pr_debug("%s: virtio not prepared to send\n", dev->name); + netif_stop_queue(dev); + + /* Activate callback for using skbs: if this returns false it + * means some were used in the meantime. */ + if (unlikely(!vi->svq->vq_ops->enable_cb(vi->svq))) { + vi->svq->vq_ops->disable_cb(vi->svq); + netif_start_queue(dev); + goto again; + } + goto done; } #ifdef CONFIG_NET_POLL_CONTROLLER @@ -355,17 +378,26 @@ static int virtnet_probe(struct virtio_device *vdev) SET_NETDEV_DEV(dev, &vdev->dev); /* Do we support "hardware" checksums? */ - if (csum && vdev->config->feature(vdev, VIRTIO_NET_F_CSUM)) { + if (csum && virtio_has_feature(vdev, VIRTIO_NET_F_CSUM)) { /* This opens up the world of extra features. */ dev->features |= NETIF_F_HW_CSUM|NETIF_F_SG|NETIF_F_FRAGLIST; - if (gso && vdev->config->feature(vdev, VIRTIO_NET_F_GSO)) { + if (gso && virtio_has_feature(vdev, VIRTIO_NET_F_GSO)) { dev->features |= NETIF_F_TSO | NETIF_F_UFO | NETIF_F_TSO_ECN | NETIF_F_TSO6; } + /* Individual feature bits: what can host handle? */ + if (gso && virtio_has_feature(vdev, VIRTIO_NET_F_HOST_TSO4)) + dev->features |= NETIF_F_TSO; + if (gso && virtio_has_feature(vdev, VIRTIO_NET_F_HOST_TSO6)) + dev->features |= NETIF_F_TSO6; + if (gso && virtio_has_feature(vdev, VIRTIO_NET_F_HOST_ECN)) + dev->features |= NETIF_F_TSO_ECN; + if (gso && virtio_has_feature(vdev, VIRTIO_NET_F_HOST_UFO)) + dev->features |= NETIF_F_UFO; } /* Configuration may specify what MAC to use. Otherwise random. */ - if (vdev->config->feature(vdev, VIRTIO_NET_F_MAC)) { + if (virtio_has_feature(vdev, VIRTIO_NET_F_MAC)) { vdev->config->get(vdev, offsetof(struct virtio_net_config, mac), dev->dev_addr, dev->addr_len); @@ -454,7 +486,15 @@ static struct virtio_device_id id_table[] = { { 0 }, }; +static unsigned int features[] = { + VIRTIO_NET_F_CSUM, VIRTIO_NET_F_GSO, VIRTIO_NET_F_MAC, + VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_HOST_TSO6, + VIRTIO_NET_F_HOST_ECN, +}; + static struct virtio_driver virtio_net = { + .feature_table = features, + .feature_table_size = ARRAY_SIZE(features), .driver.name = KBUILD_MODNAME, .driver.owner = THIS_MODULE, .id_table = id_table, |