]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/net/iseries_veth.c
[PATCH] iseries_veth: Only call dma_unmap_single() if dma_map_single() succeeded
[karo-tx-linux.git] / drivers / net / iseries_veth.c
index 13ed8dc1e91d64b5603ca71e363920fd5cf8d90b..91d79db96e821a24f4add7391a3118e72bed9f19 100644 (file)
@@ -79,6 +79,8 @@
 #include <asm/iommu.h>
 #include <asm/vio.h>
 
+#undef DEBUG
+
 #include "iseries_veth.h"
 
 MODULE_AUTHOR("Kyle Lucke <klucke@us.ibm.com>");
@@ -115,7 +117,7 @@ struct veth_msg {
        struct veth_msg *next;
        struct VethFramesData data;
        int token;
-       unsigned long in_use;
+       int in_use;
        struct sk_buff *skb;
        struct device *dev;
 };
@@ -141,7 +143,6 @@ struct veth_lpar_connection {
        struct VethCapData remote_caps;
        u32 ack_timeout;
 
-       spinlock_t msg_stack_lock;
        struct veth_msg *msg_stack_head;
 };
 
@@ -157,7 +158,6 @@ struct veth_port {
 
        rwlock_t mcast_gate;
        int promiscuous;
-       int all_mcast;
        int num_mcast;
        u64 mcast_addr[VETH_MAX_MCAST];
 };
@@ -176,33 +176,36 @@ static void veth_timed_ack(unsigned long connectionPtr);
  * Utility functions
  */
 
-#define veth_printk(prio, fmt, args...) \
-       printk(prio "%s: " fmt, __FILE__, ## args)
+#define veth_info(fmt, args...) \
+       printk(KERN_INFO "iseries_veth: " fmt, ## args)
 
 #define veth_error(fmt, args...) \
-       printk(KERN_ERR "(%s:%3.3d) ERROR: " fmt, __FILE__, __LINE__ , ## args)
+       printk(KERN_ERR "iseries_veth: Error: " fmt, ## args)
+
+#ifdef DEBUG
+#define veth_debug(fmt, args...) \
+       printk(KERN_DEBUG "iseries_veth: " fmt, ## args)
+#else
+#define veth_debug(fmt, args...) do {} while (0)
+#endif
 
+/* You must hold the connection's lock when you call this function. */
 static inline void veth_stack_push(struct veth_lpar_connection *cnx,
                                   struct veth_msg *msg)
 {
-       unsigned long flags;
-
-       spin_lock_irqsave(&cnx->msg_stack_lock, flags);
        msg->next = cnx->msg_stack_head;
        cnx->msg_stack_head = msg;
-       spin_unlock_irqrestore(&cnx->msg_stack_lock, flags);
 }
 
+/* You must hold the connection's lock when you call this function. */
 static inline struct veth_msg *veth_stack_pop(struct veth_lpar_connection *cnx)
 {
-       unsigned long flags;
        struct veth_msg *msg;
 
-       spin_lock_irqsave(&cnx->msg_stack_lock, flags);
        msg = cnx->msg_stack_head;
        if (msg)
                cnx->msg_stack_head = cnx->msg_stack_head->next;
-       spin_unlock_irqrestore(&cnx->msg_stack_lock, flags);
+
        return msg;
 }
 
@@ -278,7 +281,7 @@ static void veth_take_cap(struct veth_lpar_connection *cnx,
                                                  HvLpEvent_Type_VirtualLan);
 
        if (cnx->state & VETH_STATE_GOTCAPS) {
-               veth_error("Received a second capabilities from lpar %d\n",
+               veth_error("Received a second capabilities from LPAR %d.\n",
                           cnx->remote_lp);
                event->base_event.xRc = HvLpEvent_Rc_BufferNotAvailable;
                HvCallEvent_ackLpEvent((struct HvLpEvent *) event);
@@ -297,7 +300,7 @@ static void veth_take_cap_ack(struct veth_lpar_connection *cnx,
 
        spin_lock_irqsave(&cnx->lock, flags);
        if (cnx->state & VETH_STATE_GOTCAPACK) {
-               veth_error("Received a second capabilities ack from lpar %d\n",
+               veth_error("Received a second capabilities ack from LPAR %d.\n",
                           cnx->remote_lp);
        } else {
                memcpy(&cnx->cap_ack_event, event,
@@ -314,10 +317,15 @@ static void veth_take_monitor_ack(struct veth_lpar_connection *cnx,
        unsigned long flags;
 
        spin_lock_irqsave(&cnx->lock, flags);
-       veth_printk(KERN_DEBUG, "Monitor ack returned for lpar %d\n",
-                   cnx->remote_lp);
-       cnx->state |= VETH_STATE_RESET;
-       veth_kick_statemachine(cnx);
+       veth_debug("cnx %d: lost connection.\n", cnx->remote_lp);
+
+       /* Avoid kicking the statemachine once we're shutdown.
+        * It's unnecessary and it could break veth_stop_connection(). */
+
+       if (! (cnx->state & VETH_STATE_SHUTDOWN)) {
+               cnx->state |= VETH_STATE_RESET;
+               veth_kick_statemachine(cnx);
+       }
        spin_unlock_irqrestore(&cnx->lock, flags);
 }
 
@@ -336,8 +344,8 @@ static void veth_handle_ack(struct VethLpEvent *event)
                veth_take_monitor_ack(cnx, event);
                break;
        default:
-               veth_error("Unknown ack type %d from lpar %d\n",
-                          event->base_event.xSubtype, rlp);
+               veth_error("Unknown ack type %d from LPAR %d.\n",
+                               event->base_event.xSubtype, rlp);
        };
 }
 
@@ -373,8 +381,8 @@ static void veth_handle_int(struct VethLpEvent *event)
                veth_receive(cnx, event);
                break;
        default:
-               veth_error("Unknown interrupt type %d from lpar %d\n",
-                          event->base_event.xSubtype, rlp);
+               veth_error("Unknown interrupt type %d from LPAR %d.\n",
+                               event->base_event.xSubtype, rlp);
        };
 }
 
@@ -400,8 +408,8 @@ static int veth_process_caps(struct veth_lpar_connection *cnx)
             || (remote_caps->ack_threshold > VETH_MAX_ACKS_PER_MSG)
             || (remote_caps->ack_threshold == 0)
             || (cnx->ack_timeout == 0) ) {
-               veth_error("Received incompatible capabilities from lpar %d\n",
-                          cnx->remote_lp);
+               veth_error("Received incompatible capabilities from LPAR %d.\n",
+                               cnx->remote_lp);
                return HvLpEvent_Rc_InvalidSubtypeData;
        }
 
@@ -418,8 +426,8 @@ static int veth_process_caps(struct veth_lpar_connection *cnx)
                        cnx->num_ack_events += num;
 
                if (cnx->num_ack_events < num_acks_needed) {
-                       veth_error("Couldn't allocate enough ack events for lpar %d\n",
-                                  cnx->remote_lp);
+                       veth_error("Couldn't allocate enough ack events "
+                                       "for LPAR %d.\n", cnx->remote_lp);
 
                        return HvLpEvent_Rc_BufferNotAvailable;
                }
@@ -442,13 +450,15 @@ static void veth_statemachine(void *p)
        if (cnx->state & VETH_STATE_RESET) {
                int i;
 
-               del_timer(&cnx->ack_timer);
-
                if (cnx->state & VETH_STATE_OPEN)
                        HvCallEvent_closeLpEventPath(cnx->remote_lp,
                                                     HvLpEvent_Type_VirtualLan);
 
-               /* reset ack data */
+               /*
+                * Reset ack data. This prevents the ack_timer actually
+                * doing anything, even if it runs one more time when
+                * we drop the lock below.
+                */
                memset(&cnx->pending_acks, 0xff, sizeof (cnx->pending_acks));
                cnx->num_pending_acks = 0;
 
@@ -461,11 +471,24 @@ static void veth_statemachine(void *p)
                if (cnx->msgs)
                        for (i = 0; i < VETH_NUMBUFFERS; ++i)
                                veth_recycle_msg(cnx, cnx->msgs + i);
+
+               /* Drop the lock so we can do stuff that might sleep or
+                * take other locks. */
                spin_unlock_irq(&cnx->lock);
+
+               del_timer_sync(&cnx->ack_timer);
                veth_flush_pending(cnx);
+
                spin_lock_irq(&cnx->lock);
+
                if (cnx->state & VETH_STATE_RESET)
                        goto restart;
+
+               /* Hack, wait for the other end to reset itself. */
+               if (! (cnx->state & VETH_STATE_SHUTDOWN)) {
+                       schedule_delayed_work(&cnx->statemachine_wq, 5 * HZ);
+                       goto out;
+               }
        }
 
        if (cnx->state & VETH_STATE_SHUTDOWN)
@@ -498,9 +521,8 @@ static void veth_statemachine(void *p)
                } else {
                        if ( (rc != HvLpEvent_Rc_PartitionDead)
                             && (rc != HvLpEvent_Rc_PathClosed) )
-                               veth_error("Error sending monitor to "
-                                          "lpar %d, rc=%x\n",
-                                          rlp, (int) rc);
+                               veth_error("Error sending monitor to LPAR %d, "
+                                               "rc = %d\n", rlp, rc);
 
                        /* Oh well, hope we get a cap from the other
                         * end and do better when that kicks us */
@@ -523,9 +545,9 @@ static void veth_statemachine(void *p)
                } else {
                        if ( (rc != HvLpEvent_Rc_PartitionDead)
                             && (rc != HvLpEvent_Rc_PathClosed) )
-                               veth_error("Error sending caps to "
-                                          "lpar %d, rc=%x\n",
-                                          rlp, (int) rc);
+                               veth_error("Error sending caps to LPAR %d, "
+                                               "rc = %d\n", rlp, rc);
+
                        /* Oh well, hope we get a cap from the other
                         * end and do better when that kicks us */
                        goto out;
@@ -565,10 +587,8 @@ static void veth_statemachine(void *p)
                        add_timer(&cnx->ack_timer);
                        cnx->state |= VETH_STATE_READY;
                } else {
-                       veth_printk(KERN_ERR, "Caps rejected (rc=%d) by "
-                                   "lpar %d\n",
-                                   cnx->cap_ack_event.base_event.xRc,
-                                   rlp);
+                       veth_error("Caps rejected by LPAR %d, rc = %d\n",
+                                       rlp, cnx->cap_ack_event.base_event.xRc);
                        goto cant_cope;
                }
        }
@@ -581,8 +601,8 @@ static void veth_statemachine(void *p)
        /* FIXME: we get here if something happens we really can't
         * cope with.  The link will never work once we get here, and
         * all we can do is not lock the rest of the system up */
-       veth_error("Badness on connection to lpar %d (state=%04lx) "
-                  " - shutting down\n", rlp, cnx->state);
+       veth_error("Unrecoverable error on connection to LPAR %d, shutting down"
+                       " (state = 0x%04lx)\n", rlp, cnx->state);
        cnx->state |= VETH_STATE_SHUTDOWN;
        spin_unlock_irq(&cnx->lock);
 }
@@ -614,13 +634,12 @@ static int veth_init_connection(u8 rlp)
 
        msgs = kmalloc(VETH_NUMBUFFERS * sizeof(struct veth_msg), GFP_KERNEL);
        if (! msgs) {
-               veth_error("Can't allocate buffers for lpar %d\n", rlp);
+               veth_error("Can't allocate buffers for LPAR %d.\n", rlp);
                return -ENOMEM;
        }
 
        cnx->msgs = msgs;
        memset(msgs, 0, VETH_NUMBUFFERS * sizeof(struct veth_msg));
-       spin_lock_init(&cnx->msg_stack_lock);
 
        for (i = 0; i < VETH_NUMBUFFERS; i++) {
                msgs[i].token = i;
@@ -630,8 +649,7 @@ static int veth_init_connection(u8 rlp)
        cnx->num_events = veth_allocate_events(rlp, 2 + VETH_NUMBUFFERS);
 
        if (cnx->num_events < (2 + VETH_NUMBUFFERS)) {
-               veth_error("Can't allocate events for lpar %d, only got %d\n",
-                          rlp, cnx->num_events);
+               veth_error("Can't allocate enough events for LPAR %d.\n", rlp);
                return -ENOMEM;
        }
 
@@ -654,12 +672,17 @@ static void veth_stop_connection(u8 rlp)
        veth_kick_statemachine(cnx);
        spin_unlock_irq(&cnx->lock);
 
-       flush_scheduled_work();
+       /* There's a slim chance the reset code has just queued the
+        * statemachine to run in five seconds. If so we need to cancel
+        * that and requeue the work to run now. */
+       if (cancel_delayed_work(&cnx->statemachine_wq)) {
+               spin_lock_irq(&cnx->lock);
+               veth_kick_statemachine(cnx);
+               spin_unlock_irq(&cnx->lock);
+       }
 
-       /* FIXME: not sure if this is necessary - will already have
-        * been deleted by the state machine, just want to make sure
-        * its not running any more */
-       del_timer_sync(&cnx->ack_timer);
+       /* Wait for the state machine to run. */
+       flush_scheduled_work();
 
        if (cnx->num_events > 0)
                mf_deallocate_lp_events(cnx->remote_lp,
@@ -726,17 +749,15 @@ static void veth_set_multicast_list(struct net_device *dev)
 
        write_lock_irqsave(&port->mcast_gate, flags);
 
-       if (dev->flags & IFF_PROMISC) { /* set promiscuous mode */
-               printk(KERN_INFO "%s: Promiscuous mode enabled.\n",
-                      dev->name);
+       if ((dev->flags & IFF_PROMISC) || (dev->flags & IFF_ALLMULTI) ||
+                       (dev->mc_count > VETH_MAX_MCAST)) {
                port->promiscuous = 1;
-       } else if ( (dev->flags & IFF_ALLMULTI)
-                   || (dev->mc_count > VETH_MAX_MCAST) ) {
-               port->all_mcast = 1;
        } else {
                struct dev_mc_list *dmi = dev->mc_list;
                int i;
 
+               port->promiscuous = 0;
+
                /* Update table */
                port->num_mcast = 0;
 
@@ -802,13 +823,14 @@ static void veth_tx_timeout(struct net_device *dev)
 
        spin_lock_irqsave(&port->pending_gate, flags);
 
+       if (!port->pending_lpmask) {
+               spin_unlock_irqrestore(&port->pending_gate, flags);
+               return;
+       }
+
        printk(KERN_WARNING "%s: Tx timeout!  Resetting lp connections: %08x\n",
               dev->name, port->pending_lpmask);
 
-       /* If we've timed out the queue must be stopped, which should
-        * only ever happen when there is a pending packet. */
-       WARN_ON(! port->pending_lpmask);
-
        for (i = 0; i < HVMAXARCHITECTEDLPS; i++) {
                struct veth_lpar_connection *cnx = veth_cnx[i];
 
@@ -888,15 +910,13 @@ static struct net_device * __init veth_probe_one(int vlan, struct device *vdev)
 
        rc = register_netdev(dev);
        if (rc != 0) {
-               veth_printk(KERN_ERR,
-                           "Failed to register ethernet device for vlan %d\n",
-                           vlan);
+               veth_error("Failed registering net device for vlan%d.\n", vlan);
                free_netdev(dev);
                return NULL;
        }
 
-       veth_printk(KERN_DEBUG, "%s attached to iSeries vlan %d (lpar_map=0x%04x)\n",
-                   dev->name, vlan, port->lpar_map);
+       veth_info("%s attached to iSeries vlan %d (LPAR map = 0x%.4X)\n",
+                       dev->name, vlan, port->lpar_map);
 
        return dev;
 }
@@ -911,7 +931,6 @@ static int veth_transmit_to_one(struct sk_buff *skb, HvLpIndex rlp,
        struct veth_lpar_connection *cnx = veth_cnx[rlp];
        struct veth_port *port = (struct veth_port *) dev->priv;
        HvLpEvent_Rc rc;
-       u32 dma_address, dma_length;
        struct veth_msg *msg = NULL;
        int err = 0;
        unsigned long flags;
@@ -937,21 +956,21 @@ static int veth_transmit_to_one(struct sk_buff *skb, HvLpIndex rlp,
                goto drop;
        }
 
-       dma_length = skb->len;
-       dma_address = dma_map_single(port->dev, skb->data,
-                                    dma_length, DMA_TO_DEVICE);
+       msg->in_use = 1;
+
+       msg->data.addr[0] = dma_map_single(port->dev, skb->data,
+                               skb->len, DMA_TO_DEVICE);
 
-       if (dma_mapping_error(dma_address))
+       if (dma_mapping_error(msg->data.addr[0]))
                goto recycle_and_drop;
 
        /* Is it really necessary to check the length and address
         * fields of the first entry here? */
        msg->skb = skb;
        msg->dev = port->dev;
-       msg->data.addr[0] = dma_address;
-       msg->data.len[0] = dma_length;
+       msg->data.len[0] = skb->len;
        msg->data.eofmask = 1 << VETH_EOF_SHIFT;
-       set_bit(0, &(msg->in_use));
+
        rc = veth_signaldata(cnx, VethEventTypeFrames, msg->token, &msg->data);
 
        if (rc != HvLpEvent_Rc_Good)
@@ -961,10 +980,8 @@ static int veth_transmit_to_one(struct sk_buff *skb, HvLpIndex rlp,
        return 0;
 
  recycle_and_drop:
+       /* we free the skb below, so tell veth_recycle_msg() not to. */
        msg->skb = NULL;
-       /* need to set in use to make veth_recycle_msg in case this
-        * was a mapping failure */
-       set_bit(0, &msg->in_use);
        veth_recycle_msg(cnx, msg);
  drop:
        port->stats.tx_errors++;
@@ -1029,7 +1046,7 @@ static int veth_start_xmit(struct sk_buff *skb, struct net_device *dev)
                dev_kfree_skb(skb);
        } else {
                if (port->pending_skb) {
-                       veth_error("%s: Tx while skb was pending!\n",
+                       veth_error("%s: TX while skb was pending!\n",
                                   dev->name);
                        dev_kfree_skb(skb);
                        spin_unlock_irqrestore(&port->pending_gate, flags);
@@ -1046,17 +1063,20 @@ static int veth_start_xmit(struct sk_buff *skb, struct net_device *dev)
        return 0;
 }
 
+/* You must hold the connection's lock when you call this function. */
 static void veth_recycle_msg(struct veth_lpar_connection *cnx,
                             struct veth_msg *msg)
 {
        u32 dma_address, dma_length;
 
-       if (test_and_clear_bit(0, &msg->in_use)) {
+       if (msg->in_use) {
+               msg->in_use = 0;
                dma_address = msg->data.addr[0];
                dma_length = msg->data.len[0];
 
-               dma_unmap_single(msg->dev, dma_address, dma_length,
-                                DMA_TO_DEVICE);
+               if (!dma_mapping_error(dma_address))
+                       dma_unmap_single(msg->dev, dma_address, dma_length,
+                                       DMA_TO_DEVICE);
 
                if (msg->skb) {
                        dev_kfree_skb_any(msg->skb);
@@ -1065,10 +1085,10 @@ static void veth_recycle_msg(struct veth_lpar_connection *cnx,
 
                memset(&msg->data, 0, sizeof(msg->data));
                veth_stack_push(cnx, msg);
-       } else
-               if (cnx->state & VETH_STATE_OPEN)
-                       veth_error("Bogus frames ack from lpar %d (#%d)\n",
-                                  cnx->remote_lp, msg->token);
+       } else if (cnx->state & VETH_STATE_OPEN) {
+               veth_error("Non-pending frame (# %d) acked by LPAR %d.\n",
+                               cnx->remote_lp, msg->token);
+       }
 }
 
 static void veth_flush_pending(struct veth_lpar_connection *cnx)
@@ -1116,12 +1136,9 @@ static inline int veth_frame_wanted(struct veth_port *port, u64 mac_addr)
        if ( (mac_addr == port->mac_addr) || (mac_addr == 0xffffffffffff0000) )
                return 1;
 
-       if (! (((char *) &mac_addr)[0] & 0x01))
-               return 0;
-
        read_lock_irqsave(&port->mcast_gate, flags);
 
-       if (port->promiscuous || port->all_mcast) {
+       if (port->promiscuous) {
                wanted = 1;
                goto out;
        }
@@ -1178,8 +1195,8 @@ static void veth_flush_acks(struct veth_lpar_connection *cnx)
                             0, &cnx->pending_acks);
 
        if (rc != HvLpEvent_Rc_Good)
-               veth_error("Error 0x%x acking frames from lpar %d!\n",
-                          (unsigned)rc, cnx->remote_lp);
+               veth_error("Failed acking frames from LPAR %d, rc = %d\n",
+                               cnx->remote_lp, (int)rc);
 
        cnx->num_pending_acks = 0;
        memset(&cnx->pending_acks, 0xff, sizeof(cnx->pending_acks));
@@ -1215,9 +1232,10 @@ static void veth_receive(struct veth_lpar_connection *cnx,
                /* make sure that we have at least 1 EOF entry in the
                 * remaining entries */
                if (! (senddata->eofmask >> (startchunk + VETH_EOF_SHIFT))) {
-                       veth_error("missing EOF frag in event "
-                                  "eofmask=0x%x startchunk=%d\n",
-                                  (unsigned) senddata->eofmask, startchunk);
+                       veth_error("Missing EOF fragment in event "
+                                       "eofmask = 0x%x startchunk = %d\n",
+                                       (unsigned)senddata->eofmask,
+                                       startchunk);
                        break;
                }
 
@@ -1236,8 +1254,9 @@ static void veth_receive(struct veth_lpar_connection *cnx,
                /* nchunks == # of chunks in this frame */
 
                if ((length - ETH_HLEN) > VETH_MAX_MTU) {
-                       veth_error("Received oversize frame from lpar %d "
-                                  "(length=%d)\n", cnx->remote_lp, length);
+                       veth_error("Received oversize frame from LPAR %d "
+                                       "(length = %d)\n",
+                                       cnx->remote_lp, length);
                        continue;
                }
 
@@ -1369,7 +1388,7 @@ static int veth_probe(struct vio_dev *vdev, const struct vio_device_id *id)
  */
 static struct vio_device_id veth_device_table[] __devinitdata = {
        { "vlan", "" },
-       { NULL, NULL }
+       { "", "" }
 };
 MODULE_DEVICE_TABLE(vio, veth_device_table);