lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <1297937599.2670.44.camel@edumazet-laptop>
Date:	Thu, 17 Feb 2011 11:13:19 +0100
From:	Eric Dumazet <eric.dumazet@...il.com>
To:	Andreas Fenkart <afenkart@...il.com>
Cc:	netdev@...r.kernel.org
Subject: Re: [PATCH 1/1] ARC VMAC ethernet driver.

Le jeudi 17 février 2011 à 10:31 +0100, Andreas Fenkart a écrit :
> Signed-off-by: Andreas Fenkart <afenkart@...il.com>
> ---
>  drivers/net/Kconfig   |   10 +
>  drivers/net/Makefile  |    1 +
>  drivers/net/arcvmac.c | 1494 +++++++++++++++++++++++++++++++++++++++++++++++++
>  drivers/net/arcvmac.h |  265 +++++++++
>  4 files changed, 1770 insertions(+), 0 deletions(-)
> 



> +/* merge buffer chaining  */
> +struct sk_buff *vmac_merge_rx_buffers_unlocked(struct net_device *dev,
> +		struct vmac_buffer_desc *after,
> +		int pkt_len) /* data */
> +{
> +	struct vmac_priv *ap = netdev_priv(dev);
> +	struct sk_buff *merge_skb, *cur_skb;
> +	struct dma_fifo *rx_ring;
> +	struct vmac_buffer_desc *desc;
> +
> +	/* locking: same as vmac_rx_receive */
> +
> +	rx_ring = &ap->rx_ring;
> +	desc = &ap->rxbd[rx_ring->tail];
> +
> +	WARN_ON(desc == after);
> +
> +	/* strip FCS */
> +	pkt_len -= 4;
> +
> +	merge_skb = netdev_alloc_skb_ip_align(dev, pkt_len + NET_IP_ALIGN);


You can remove the "+ NET_IP_ALIGN", its already done in
netdev_alloc_skb_ip_align()

Also, it seems strange you want to build one big SKB (no frag), while
this NIC is able to feed multiple frags.

(Change to get a SKB to hold a 9000 bytes frame is very very low if your
memory gets fragmented)

> +	if (!merge_skb) {
> +		dev_err(&ap->pdev->dev, "failed to allocate merged rx_skb, rx skb's left %d\n",
> +				fifo_used(rx_ring));
> +
> +		return NULL;
> +	}
> +
> +	while (desc != after && pkt_len) {
> +		struct vmac_buffer_desc *desc;
> +		int buf_len, valid;
> +
> +		/* desc needs wrapping */
> +		desc = &ap->rxbd[rx_ring->tail];
> +		cur_skb = ap->rx_skbuff[rx_ring->tail];
> +		WARN_ON(!cur_skb);
> +
> +		dma_unmap_single(&ap->pdev->dev, desc->data, ap->rx_skb_size,
> +				DMA_FROM_DEVICE);
> +
> +		/* do not copy FCS */
> +		buf_len = le32_to_cpu(desc->info) & BD_LEN;
> +		valid = min(pkt_len, buf_len);
> +		pkt_len -= valid;
> +
> +		memcpy(skb_put(merge_skb, valid), cur_skb->data, valid);
> +
> +		fifo_inc_tail(rx_ring);
> +	}
> +
> +	/* merging_pressure++ */
> +
> +	if (unlikely(pkt_len != 0))
> +		dev_err(&ap->pdev->dev, "buffer chaining bytes missing %d\n",
> +				pkt_len);
> +
> +	WARN_ON(desc != after);
> +
> +	return merge_skb;
> +}
> +

> +
> +int vmac_start_xmit(struct sk_buff *skb, struct net_device *dev)
> +{
> +	struct vmac_priv *ap = netdev_priv(dev);
> +	struct vmac_buffer_desc *desc;
> +	unsigned long flags;
> +	unsigned int tmp;
> +
> +	/* running under xmit lock */
> +	/* locking: modifies tx_ring head, tx_reclaim only tail */
> +
> +	/* no scatter/gatter see features below */
> +	WARN_ON(skb_shinfo(skb)->nr_frags != 0);
> +	WARN_ON(skb->len > MAX_TX_BUFFER_LEN);
> +
> +	if (unlikely(fifo_full(&ap->tx_ring))) {
> +		netif_stop_queue(dev);
> +		vmac_toggle_txint(dev, 1);
> +		dev_err(&ap->pdev->dev, "xmit called with no tx desc available\n");
> +		return NETDEV_TX_BUSY;
> +	}
> +
> +	if (unlikely(skb->len < ETH_ZLEN)) {
> +		struct sk_buff *short_skb;
> +		short_skb = netdev_alloc_skb_ip_align(dev, ETH_ZLEN);

I guess you dont really need the _ip_align() version here

> +		if (!short_skb)
> +			return NETDEV_TX_LOCKED;
> +
> +		memset(short_skb->data, 0, ETH_ZLEN);
> +		memcpy(skb_put(short_skb, ETH_ZLEN), skb->data, skb->len);
> +		dev_kfree_skb(skb);
> +		skb = short_skb;
> +	}
> +
> +	/* fill descriptor */
> +	ap->tx_skbuff[ap->tx_ring.head] = skb;
> +	desc = &ap->txbd[ap->tx_ring.head];
> +	WARN_ON(desc->info & cpu_to_le32(BD_DMA_OWN));
> +
> +	desc->data = dma_map_single(&ap->pdev->dev, skb->data, skb->len,
> +			DMA_TO_DEVICE);
> +
> +	/* dma might already be polling */
> +	wmb();
> +	desc->info = cpu_to_le32(BD_DMA_OWN | BD_FRST | BD_LAST | skb->len);
> +	wmb();

Not sure you need this wmb();

> +
> +	/* lock device data */
> +	spin_lock_irqsave(&ap->lock, flags);
> +
> +	/* kick tx dma */
> +	tmp = vmac_readl(ap, STAT);
> +	vmac_writel(ap, tmp | TXPL_MASK, STAT);
> +
> +	dev->stats.tx_packets++;
> +	dev->stats.tx_bytes += skb->len;
> +	dev->trans_start = jiffies;

trans_start doesnt need to be set anymore in drivers.

> +	fifo_inc_head(&ap->tx_ring);
> +
> +	/* vmac_tx_reclaim outside of vmac_tx_timeout */
> +	if (fifo_used(&ap->tx_ring) > 8)
> +		vmac_tx_reclaim_unlocked(dev, 0);
> +
> +	/* unlock device data */
> +	spin_unlock_irqrestore(&ap->lock, flags);
> +
> +	/* stop queue if no more desc available */
> +	if (fifo_full(&ap->tx_ring)) {
> +		netif_stop_queue(dev);
> +		vmac_toggle_txint(dev, 1);
> +	}
> +
> +	return NETDEV_TX_OK;
> +}
> +


> +static void create_multicast_filter(struct net_device *dev,
> +	unsigned long *bitmask)
> +{
> +	unsigned long crc;
> +	char *addrs;
> +
> +	/* locking: done by net_device */
> +
> +	WARN_ON(netdev_mc_count(dev) == 0);
> +	WARN_ON(dev->flags & IFF_ALLMULTI);
> +
> +	bitmask[0] = bitmask[1] = 0;
> +
> +	{
> +		struct netdev_hw_addr *ha;
> +		netdev_for_each_mc_addr(ha, dev) {
> +			addrs = ha->addr;
> +
> +			/* skip non-multicast addresses */
> +			if (!(*addrs & 1))
> +				continue;
> +
> +			crc = ether_crc_le(ETH_ALEN, addrs);
> +			set_bit(crc >> 26, bitmask);

I am wondering if it works on 64bit arches ;)

> +		}
> +	}
> +}
> +

> +static struct ethtool_ops vmac_ethtool_ops = {

please add const qualifier

static const struct ethtool_ops vmac_ethtool_ops = {


> +	.get_settings		= vmacether_get_settings,
> +	.set_settings		= vmacether_set_settings,
> +	.get_drvinfo		= vmacether_get_drvinfo,
> +	.get_link		= ethtool_op_get_link,
> +};
> +

> +static int __devinit vmac_probe(struct platform_device *pdev)
> +{
> +	struct net_device *dev;
> +	struct vmac_priv *ap;
> +	struct resource *mem;
> +	int err;
> +
> +	/* locking: no concurrency */
> +
> +	if (dma_get_mask(&pdev->dev) > DMA_BIT_MASK(32) ||
> +			pdev->dev.coherent_dma_mask > DMA_BIT_MASK(32)) {
> +		dev_err(&pdev->dev, "arcvmac supports only 32-bit DMA addresses\n");
> +		return -ENODEV;
> +	}
> +
> +	dev = alloc_etherdev(sizeof(*ap));
> +	if (!dev) {
> +		dev_err(&pdev->dev, "etherdev alloc failed, aborting.\n");
> +		return -ENOMEM;
> +	}
> +
> +	ap = netdev_priv(dev);
> +
> +	err = -ENODEV;
> +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!mem) {
> +		dev_err(&pdev->dev, "no mmio resource defined\n");
> +		goto err_out;
> +	}
> +	ap->mem = mem;
> +
> +	err = platform_get_irq(pdev, 0);
> +	if (err < 0) {
> +		dev_err(&pdev->dev, "no irq found\n");
> +		goto err_out;
> +	}
> +	dev->irq = err;
> +
> +	spin_lock_init(&ap->lock);
> +
> +	SET_NETDEV_DEV(dev, &pdev->dev);
> +	ap->dev = dev;
> +	ap->pdev = pdev;
> +
> +	/* init rx timeout (used for oom) */
> +	init_timer(&ap->refill_timer);
> +	ap->refill_timer.function = vmac_refill_rx_timer;
> +	ap->refill_timer.data = (unsigned long)dev;
> +	spin_lock_init(&ap->refill_lock);
> +
> +	netif_napi_add(dev, &ap->napi, vmac_poll, 2);

2 ?

You have 16 skb in RX ring, please use 16 (or 64)

> +	dev->netdev_ops = &vmac_netdev_ops;
> +	dev->ethtool_ops = &vmac_ethtool_ops;
> +
> +	dev->flags |= IFF_MULTICAST;
> +
> +	dev->base_addr = (unsigned long)ap->regs; /* TODO */
> +
> +	/* prevent buffer chaining, favor speed over space */
> +	ap->rx_skb_size = ETH_FRAME_LEN + VMAC_BUFFER_PAD;
> +
> +	/* private struct functional */
> +
> +	/* temporarily map registers to fetch mac addr */
> +	err = get_register_map(ap);
> +	if (err)
> +		goto err_out;
> +
> +	/* mac address intialize, set vmac_open  */
> +	read_mac_reg(dev, dev->dev_addr); /* TODO */
> +
> +	if (!is_valid_ether_addr(dev->dev_addr))
> +		random_ether_addr(dev->dev_addr);
> +
> +	err = register_netdev(dev);
> +	if (err) {
> +		dev_err(&pdev->dev, "Cannot register net device, aborting.\n");
> +		goto err_out;
> +	}
> +
> +	/* release the memory region, till open is called */
> +	put_register_map(ap);
> +
> +	dev_info(&pdev->dev, "ARC VMAC at 0x%pP irq %d %pM\n", &mem->start,
> +	    dev->irq, dev->dev_addr);
> +	platform_set_drvdata(pdev, ap);
> +
> +	return 0;
> +
> +err_out:
> +	free_netdev(dev);
> +	return err;
> +}
> +

> diff --git a/drivers/net/arcvmac.h b/drivers/net/arcvmac.h
> new file mode 100644
> index 0000000..ee570a5
> --- /dev/null
> +++ b/drivers/net/arcvmac.h
> @@ -0,0 +1,265 @@
> +/*
> + * ARC VMAC Driver
> + *
> + * Copyright (C) 2003-2006 Codito Technologies, for linux-2.4 port
> + * Copyright (C) 2006-2007 Celunite Inc, for linux-2.6 port
> + * Copyright (C) 2007-2008 Sagem Communications, Fehmi HAFSI
> + * Copyright (C) 2009-2011 Sagem Communications, Andreas Fenkart
> + * All Rights Reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> + *
> + */
> +
> +#ifndef _ARCVMAC_H
> +#define _ARCVMAC_H
> +
> +#define DRV_NAME		"arcvmac"
> +#define DRV_VERSION		"1.0"
> +
> +/* Buffer descriptors */
> +#define TX_BDT_LEN		16    /* Number of receive BD's */

16 is a bit small. Is it a hardware limitation or user choice ?

> +#define RX_BDT_LEN		256   /* Number of transmit BD's */

If hardware permits it, I suggest using 128 RX and 128 TX descs

> +
> +/* BD poll rate, in 1024 cycles. @100Mhz: x * 1024 cy * 10ns = 1ms */
> +#define POLLRATE_TIME		200
> +
> +/* next power of two, bigger than ETH_FRAME_LEN + VLAN  */
> +#define MAX_RX_BUFFER_LEN	0x800	/* 2^11 = 2048 = 0x800 */
> +#define MAX_TX_BUFFER_LEN	0x800	/* 2^11 = 2048 = 0x800 */
> +
> +/* 14 bytes of ethernet header, 4 bytes VLAN, FCS,
> + * plus extra pad to prevent buffer chaining of
> + * maximum sized ethernet packets (1514 bytes) */
> +#define	VMAC_BUFFER_PAD		(ETH_HLEN + 4 + ETH_FCS_LEN + 4)
> +
> +/* VMAC register definitions, offsets in bytes */
> +#define VMAC_ID			0x00
> +
> +/* stat/enable use same bit mask */
> +#define VMAC_STAT		0x04
> +#define VMAC_ENABLE		0x08
> +#  define TXINT_MASK		0x00000001 /* Transmit interrupt */
> +#  define RXINT_MASK		0x00000002 /* Receive interrupt */
> +#  define ERR_MASK		0x00000004 /* Error interrupt */
> +#  define TXCH_MASK		0x00000008 /* Transmit chaining error */
> +#  define MSER_MASK		0x00000010 /* Missed packet counter error */
> +#  define RXCR_MASK		0x00000100 /* RXCRCERR counter rolled over	 */
> +#  define RXFR_MASK		0x00000200 /* RXFRAMEERR counter rolled over */
> +#  define RXFL_MASK		0x00000400 /* RXOFLOWERR counter rolled over */
> +#  define MDIO_MASK		0x00001000 /* MDIO complete */
> +#  define TXPL_MASK		0x80000000 /* TXPOLL */
> +
> +#define VMAC_CONTROL		0x0c
> +#  define EN_MASK		0x00000001 /* VMAC enable */
> +#  define TXRN_MASK		0x00000008 /* TX enable */
> +#  define RXRN_MASK		0x00000010 /* RX enable */
> +#  define DSBC_MASK		0x00000100 /* Disable receive broadcast */
> +#  define ENFL_MASK		0x00000400 /* Enable Full Duplex */
> +#  define PROM_MASK		0x00000800 /* Promiscuous mode */
> +
> +#define VMAC_POLLRATE		0x10
> +
> +#define VMAC_RXERR		0x14
> +#  define RXERR_CRC		0x000000ff
> +#  define RXERR_FRM		0x0000ff00
> +#  define RXERR_OFLO		0x00ff0000 /* fifo overflow */
> +
> +#define VMAC_MISS		0x18
> +#define VMAC_TXRINGPTR		0x1c
> +#define VMAC_RXRINGPTR		0x20
> +#define VMAC_ADDRL		0x24
> +#define VMAC_ADDRH		0x28
> +#define VMAC_LAFL		0x2c
> +#define VMAC_LAFH		0x30
> +#define VMAC_MAC_TXRING_HEAD	0x38
> +#define VMAC_MAC_RXRING_HEAD	0x3C
> +
> +#define VMAC_MDIO_DATA		0x34
> +#  define MDIO_SFD		0xC0000000
> +#  define MDIO_OP		0x30000000
> +#  define MDIO_ID_MASK		0x0F800000
> +#  define MDIO_REG_MASK		0x007C0000
> +#  define MDIO_TA		0x00030000
> +#  define MDIO_DATA_MASK	0x0000FFFF
> +/* common combinations */
> +#  define MDIO_BASE		0x40020000
> +#  define MDIO_OP_READ		0x20000000
> +#  define MDIO_OP_WRITE		0x10000000
> +
> +/* Buffer descriptor INFO bit masks */
> +#define BD_DMA_OWN		0x80000000 /* buffer ownership, 0 CPU, 1 DMA */
> +#define BD_BUFF			0x40000000 /* buffer invalid, rx */
> +#define BD_UFLO			0x20000000 /* underflow, tx */
> +#define BD_LTCL			0x10000000 /* late collision, tx  */
> +#define BD_RETRY_CT		0x0f000000 /* tx */
> +#define BD_DROP			0x00800000 /* drop, more than 16 retries, tx */
> +#define BD_DEFER		0x00400000 /* traffic on the wire, tx */
> +#define BD_CARLOSS		0x00200000 /* carrier loss while transmission, tx, rx? */
> +/* 20:19 reserved */
> +#define BD_ADCR			0x00040000 /* add crc, ignored if not disaddcrc */
> +#define BD_LAST			0x00020000 /* Last buffer in chain */
> +#define BD_FRST			0x00010000 /* First buffer in chain */
> +/* 15:11 reserved */
> +#define BD_LEN			0x000007FF
> +
> +/* common combinations */
> +#define BD_TX_ERR		(BD_UFLO | BD_LTCL | BD_RETRY_CT | BD_DROP | \
> +		BD_DEFER | BD_CARLOSS)
> +
> +
> +/* arcvmac private data structures */
> +struct vmac_buffer_desc {
> +	__le32 info;
> +	__le32 data;
> +};
> +
> +struct dma_fifo {
> +	int head; /* head */
> +	int tail; /* tail */
> +	int size;
> +};
> +
> +struct	vmac_priv {
> +	struct net_device *dev;
> +	struct platform_device *pdev;
> +
> +	struct completion mdio_complete;
> +	spinlock_t lock; /* protects structure plus hw regs of device */
> +
> +	/* base address of register set */
> +	char *regs;
> +	struct resource *mem;
> +
> +	/* DMA ring buffers */
> +	struct vmac_buffer_desc *rxbd;
> +	dma_addr_t rxbd_dma;
> +
> +	struct vmac_buffer_desc *txbd;
> +	dma_addr_t txbd_dma;
> +
> +	/* socket buffers */
> +	struct sk_buff *rx_skbuff[RX_BDT_LEN];
> +	struct sk_buff *tx_skbuff[TX_BDT_LEN];
> +	int rx_skb_size;
> +
> +	/* skb / dma desc managing */
> +	struct dma_fifo rx_ring; /* valid rx buffers */
> +	struct dma_fifo tx_ring;
> +
> +	/* descriptor last polled/processed by the VMAC */
> +	unsigned long dma_rx_head;
> +
> +	/* timer to retry rx skb allocation, if failed during receive */
> +	struct timer_list refill_timer;
> +	spinlock_t refill_lock;
> +
> +	struct napi_struct napi;
> +
> +	/* rx buffer chaining */
> +	int rx_merge_error;
> +	int tx_timeout_error;
> +
> +	/* PHY stuff */
> +	struct mii_bus *mii_bus;
> +	struct phy_device *phy_dev;
> +
> +	int link;
> +	int speed;
> +	int duplex;
> +
> +	/* debug */
> +	int shutdown;
> +};
> +
> +/* DMA ring management */
> +
> +/* for a fifo with size n,
> + * - [0..n] fill levels are n + 1 states
> + * - there are only n different deltas (head - tail) values
> + * => not all fill levels can be represented with head, tail
> + *    pointers only
> + * we give up the n fill level, aka fifo full */
> +
> +/* sacrifice one elt as a sentinel */
> +static inline int fifo_used(struct dma_fifo *f);
> +static inline int fifo_inc_ct(int ct, int size);
> +static inline void fifo_dump(struct dma_fifo *fifo);
> +
> +static inline int fifo_empty(struct dma_fifo *f)
> +{
> +	return (f->head == f->tail);

return f->head == f->tail;

> +}
> +
> +static inline int fifo_free(struct dma_fifo *f)
> +{
> +	int free;
> +
> +	free = f->tail - f->head;
> +	if (free <= 0)
> +		free += f->size;
> +
> +	return free;
> +}
> +
> +static inline int fifo_used(struct dma_fifo *f)
> +{
> +	int used;
> +
> +	used = f->head - f->tail;
> +	if (used < 0)
> +		used += f->size;
> +
> +	return used;
> +}
> +
> +static inline int fifo_full(struct dma_fifo *f)
> +{
> +	return (fifo_used(f) + 1) == f->size;
> +}
> +
> +/* manipulate */
> +static inline void fifo_init(struct dma_fifo *fifo, int size)
> +{
> +	fifo->size = size;
> +	fifo->head = fifo->tail = 0; /* empty */
> +}
> +
> +static inline void fifo_inc_head(struct dma_fifo *fifo)
> +{
> +	BUG_ON(fifo_full(fifo));
> +	fifo->head = fifo_inc_ct(fifo->head, fifo->size);
> +}
> +
> +static inline void fifo_inc_tail(struct dma_fifo *fifo)
> +{
> +	BUG_ON(fifo_empty(fifo));
> +	fifo->tail = fifo_inc_ct(fifo->tail, fifo->size);
> +}
> +
> +/* internal funcs */
> +static inline void fifo_dump(struct dma_fifo *fifo)
> +{
> +	printk(KERN_INFO "fifo: head %d, tail %d, size %d\n", fifo->head,
> +			fifo->tail,
> +			fifo->size);

	pr_info() is preferred in new code
> +}
> +
> +static inline int fifo_inc_ct(int ct, int size)
> +{
> +	return (++ct == size) ? 0 : ct;
> +}
> +
> +#endif	  /* _ARCVMAC_H */


--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ