Merge branch 'sunvnet-next'
authorDavid S. Miller <davem@davemloft.net>
Tue, 11 Nov 2014 02:05:43 +0000 (21:05 -0500)
committerDavid S. Miller <davem@davemloft.net>
Tue, 11 Nov 2014 02:05:43 +0000 (21:05 -0500)
Sowmini Varadhan says:

====================
sunvnet: edge-case/race-conditions bug fixes

This patch series contains fixes for race-conditions in sunvnet,
that can encountered when there is a difference in latency between
producer and consumer.

Patch 1 addresses a case when the STOPPED LDC ack from a peer is
processed before vnet_start_xmit can finish updating the dr->prod
state.

Patch 2 fixes the edge-case when outgoing data and incoming
stopped-ack cross each other in flight.

Patch 3 adds a missing rcu_read_unlock(), found by code-inspection.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/sun/sunvnet.c

index 5c5fb59..55d66c9 100644 (file)
@@ -559,18 +559,20 @@ static int vnet_ack(struct vnet_port *port, void *msgbuf)
                return 0;
 
        end = pkt->end_idx;
-       if (unlikely(!idx_is_pending(dr, end)))
-               return 0;
-
        vp = port->vp;
        dev = vp->dev;
+       netif_tx_lock(dev);
+       if (unlikely(!idx_is_pending(dr, end))) {
+               netif_tx_unlock(dev);
+               return 0;
+       }
+
        /* sync for race conditions with vnet_start_xmit() and tell xmit it
         * is time to send a trigger.
         */
-       netif_tx_lock(dev);
        dr->cons = next_idx(end, dr);
        desc = vio_dring_entry(dr, dr->cons);
-       if (desc->hdr.state == VIO_DESC_READY && port->start_cons) {
+       if (desc->hdr.state == VIO_DESC_READY && !port->start_cons) {
                /* vnet_start_xmit() just populated this dring but missed
                 * sending the "start" LDC message to the consumer.
                 * Send a "start" trigger on its behalf.
@@ -979,8 +981,10 @@ static int vnet_start_xmit(struct sk_buff *skb, struct net_device *dev)
 
        rcu_read_lock();
        port = __tx_port_find(vp, skb);
-       if (unlikely(!port))
+       if (unlikely(!port)) {
+               rcu_read_unlock();
                goto out_dropped;
+       }
 
        if (skb->len > port->rmtu) {
                unsigned long localmtu = port->rmtu - ETH_HLEN;