[PATCH 1/3] ethernet: Add new driver for Marvell Armada 375 network unit

Francois Romieu romieu at fr.zoreil.com
Sat Jul 5 14:03:59 PDT 2014


Partial review below.

> diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c
> new file mode 100644
> index 0000000..154b866
> --- /dev/null
> +++ b/drivers/net/ethernet/marvell/mvpp2.c
[...]
> +static int mvpp2_prs_tcam_first_free(struct mvpp2 *pp2, int start, int end)
> +{
> +	int tid;
> +	bool found = false;
> +
> +	if (start < end)
> +		for (tid = start; tid <= end; tid++) {
> +			if (!pp2->prs_shadow[tid].valid) {
> +				found = true;
> +				break;
> +			}
> +		}
> +	else
> +		for (tid = start; tid >= end; tid--) {
> +			if (!pp2->prs_shadow[tid].valid) {
> +				found = true;
> +				break;
> +			}
> +		}

Missing parenthsesis in "if ... else ..." block.

[...]
> +static void mvpp2_defaults_set(struct mvpp2_port *pp)
> +{
[...]
> +	/* At default, mask all interrupts to all present cpus */
> +	for_each_present_cpu(cpu)
> +		mvpp2_cpu_interrupts_disable(pp, cpu);

Would it hurt to issue a single write and disable all irqs ?

[...]
> +static int mvpp2_tx_frag_process(struct mvpp2_port *pp, struct sk_buff *skb,
> +				 struct mvpp2_tx_queue *aggr_txq,
> +				 struct mvpp2_tx_queue *txq)
> +{
[...]
> +error:
> +	/* Release all descriptors that were used to map fragments of
> +	 * this packet, as well as the corresponding DMA mappings
> +	 */
> +	for (i = i - 1; i >= 0; i--) {

You may consider "while (--i >= 0) {" as an idiom to balance a
"for (i = 0; .." loop. Your call.

> +		tx_desc = txq->descs + i;
> +		dma_unmap_single(pp->dev->dev.parent,
> +				 tx_desc->buf_phys_addr,
> +				 tx_desc->data_size,
> +				 DMA_TO_DEVICE);

		dma_unmap_single(pp->dev->dev.parent, tx_desc->buf_phys_addr,
				 tx_desc->data_size, DMA_TO_DEVICE);

You may also factor out mvpp2_dma_unmap_single(pp, tx_desc) or the whole
dma_unmap_single + mvpp2_txq_desc_put sequence.

[...]
> +/* Main tx processing */
> +static int mvpp2_tx(struct sk_buff *skb, struct net_device *dev)
> +{
> +	struct mvpp2_port *pp = netdev_priv(dev);
> +	struct mvpp2_tx_queue *txq, *aggr_txq;
> +	struct mvpp2_txq_pcpu *txq_pcpu;
> +	struct mvpp2_tx_desc *tx_desc;
> +	dma_addr_t buf_phys_addr;
> +	int frags = 0;
> +	u16 txq_id;
> +	u32 tx_cmd;
> +
> +	if (!netif_running(dev))
> +		goto out;

The driver is expected to netif_stop_queue when the device goes down, not
the opposite.

[...]
> +static int mvpp2_poll(struct napi_struct *napi, int budget)
> +{
> +	u32 cause_rx_tx, cause_rx;
> +	int rx_done = 0;
> +	struct mvpp2_port *pp = netdev_priv(napi->dev);
> +
> +	if (!netif_running(pp->dev)) {
> +		napi_complete(napi);
> +		return rx_done;
> +	}

Same thing as above.

[...]
> +static int mvpp2_ethtool_set_ringparam(struct net_device *dev,
> +				       struct ethtool_ringparam *ring)
> +{
> +	struct mvpp2_port *pp = netdev_priv(dev);
> +
> +	if ((ring->rx_pending == 0) || (ring->tx_pending == 0))
> +		return -EINVAL;
> +	pp->rx_ring_size = ring->rx_pending < MVPP2_MAX_RXD ?
> +		ring->rx_pending : MVPP2_MAX_RXD;
> +	pp->tx_ring_size = ring->tx_pending < MVPP2_MAX_TXD ?
> +		ring->tx_pending : MVPP2_MAX_TXD;
> +
> +	if (netif_running(dev)) {
> +		mvpp2_stop(dev);
> +		if (mvpp2_open(dev)) {

You aren't supposed to (ab)use net_device_ops.{ndo_open / stop} like that.
mvpp2_tx() is still racing with mvpp2_cleanup_txqs(). Even if you go through
the hassle of (1) avoiding this race, then (2) enforcing a safe double call
of mvpp2_stop without any mvpp2_open inbetween, nobody wants its device to
become unresponsive because of a failed ring parameter change: the driver
must stop and configure the physical device more gently.

Depending on the rework, you may consider postponing ethtool ring change to
a separate patch series.

[...]
> +static const struct net_device_ops mvpp2_netdev_ops = {
> +	.ndo_open            = mvpp2_open,
> +	.ndo_stop            = mvpp2_stop,
> +	.ndo_start_xmit      = mvpp2_tx,
> +	.ndo_set_rx_mode     = mvpp2_set_rx_mode,
> +	.ndo_set_mac_address = mvpp2_set_mac_address,
> +	.ndo_change_mtu      = mvpp2_change_mtu,
> +	.ndo_get_stats64     = mvpp2_get_stats64,
> +};

Please replace spaces by tabs before "=".

> +
> +static const struct ethtool_ops mvpp2_eth_tool_ops = {
> +	.get_link       = ethtool_op_get_link,
> +	.get_settings   = mvpp2_ethtool_get_settings,
> +	.set_settings   = mvpp2_ethtool_set_settings,
> +	.set_coalesce   = mvpp2_ethtool_set_coalesce,
> +	.get_coalesce   = mvpp2_ethtool_get_coalesce,
> +	.get_drvinfo    = mvpp2_ethtool_get_drvinfo,
> +	.get_ringparam  = mvpp2_ethtool_get_ringparam,
> +	.set_ringparam	= mvpp2_ethtool_set_ringparam,
> +};

Same as above.

[...]
> +static int mvpp2_port_init(struct mvpp2_port *pp)
> +{
> +	struct device *dev = pp->dev->dev.parent;
> +	struct mvpp2 *pp2 = pp->pp2;
> +	struct mvpp2_txq_pcpu *txq_pcpu;
> +	int queue, txp, cpu;
> +
> +	if (pp->first_rxq + rxq_number > MVPP2_RXQ_TOTAL_NUM)
> +		return -EINVAL;
> +
> +	/* Disable port */
> +	mvpp2_egress_disable(pp);
> +	mvpp2_port_disable(pp);
> +
> +	pp->txqs = devm_kzalloc(dev, pp->txp_num * txq_number *
> +				sizeof(struct mvpp2_tx_queue *), GFP_KERNEL);
> +	if (!pp->txqs)
> +		return -ENOMEM;
> +
> +	/* Associate physical Tx queues to this port and initialize.
> +	 * The mapping is predefined.
> +	 */
> +	for (txp = 0; txp < pp->txp_num; txp++) {
> +		for (queue = 0; queue < txq_number; queue++) {
> +			int txq_idx = txp * txq_number + queue;
> +			int queue_phy_id = mvpp2_txq_phys(pp->id, queue);
> +			struct mvpp2_tx_queue *txq;
> +
> +			txq = devm_kzalloc(dev, sizeof(*txq), GFP_KERNEL);
> +			if (!txq)
> +				return -ENOMEM;
> +
> +			txq->pcpu = alloc_percpu(struct mvpp2_txq_pcpu);
> +			if (!txq->pcpu)
> +				return -ENOMEM;

There is a per_cpu leak if mvpp2_port_init fails.

[...]
> +/* Ports initialization */
> +static int mvpp2_port_probe(struct platform_device *pdev,
> +			    struct device_node *port_node,
> +			    struct mvpp2 *pp2,
> +			    int *next_first_rxq)
> +{
> +	struct device_node *phy_node;
> +	struct mvpp2_port *pp;

(nit below)

I am not a huge fan of the "pp2" variable as there's no "pp1" but I've seen
worse. However naming "pp" some completely unrelated data imho verges on
confusing

[...]
> +	dev->irq = irq_of_parse_and_map(port_node, 0);
> +	if (dev->irq == 0) {
> +		err = -EINVAL;
> +		goto err_free_netdev;
> +	}

net_device.irq should be considered legacy. It would be nice to avoid
more uses of it. You may store it near pp->base for instance.

-- 
Ueimor



More information about the linux-arm-kernel mailing list