Index: trunk/src/VBox/HostDrivers/VBoxNetFlt/linux/VBoxNetFlt-linux.c
===================================================================
--- trunk/src/VBox/HostDrivers/VBoxNetFlt/linux/VBoxNetFlt-linux.c	(revision 28088)
+++ trunk/src/VBox/HostDrivers/VBoxNetFlt/linux/VBoxNetFlt-linux.c	(revision 28140)
@@ -50,44 +50,39 @@
 #include "../VBoxNetFltInternal.h"
 
-#define VBOX_FLT_NB_TO_INST(pNB) ((PVBOXNETFLTINS)((uint8_t *)pNB - \
-                                  RT_OFFSETOF(VBOXNETFLTINS, u.s.Notifier)))
-#define VBOX_FLT_PT_TO_INST(pPT) ((PVBOXNETFLTINS)((uint8_t *)pPT - \
-                                  RT_OFFSETOF(VBOXNETFLTINS, u.s.PacketType)))
-#define VBOX_FLT_XT_TO_INST(pXT) ((PVBOXNETFLTINS)((uint8_t *)pXT - \
-                                  RT_OFFSETOF(VBOXNETFLTINS, u.s.XmitTask)))
-
-#define VBOX_GET_PCOUNT(pDev) (pDev->promiscuity)
+
+/*******************************************************************************
+*   Defined Constants And Macros                                               *
+*******************************************************************************/
+#define VBOX_FLT_NB_TO_INST(pNB) ((PVBOXNETFLTINS)((uint8_t *)pNB - RT_OFFSETOF(VBOXNETFLTINS, u.s.Notifier)))
+#define VBOX_FLT_PT_TO_INST(pPT) ((PVBOXNETFLTINS)((uint8_t *)pPT - RT_OFFSETOF(VBOXNETFLTINS, u.s.PacketType)))
+#define VBOX_FLT_XT_TO_INST(pXT) ((PVBOXNETFLTINS)((uint8_t *)pXT - RT_OFFSETOF(VBOXNETFLTINS, u.s.XmitTask)))
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22)
-# define VBOX_SKB_RESET_NETWORK_HDR(skb) skb_reset_network_header(skb)
-# define VBOX_SKB_RESET_MAC_HDR(skb) skb_reset_mac_header(skb)
-#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) */
-# define VBOX_SKB_RESET_NETWORK_HDR(skb) skb->nh.raw = skb->data
-# define VBOX_SKB_RESET_MAC_HDR(skb) skb->mac.raw = skb->data
-#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) */
+# define VBOX_SKB_RESET_NETWORK_HDR(skb)    skb_reset_network_header(skb)
+# define VBOX_SKB_RESET_MAC_HDR(skb)        skb_reset_mac_header(skb)
+#else
+# define VBOX_SKB_RESET_NETWORK_HDR(skb)    skb->nh.raw = skb->data
+# define VBOX_SKB_RESET_MAC_HDR(skb)        skb->mac.raw = skb->data
+#endif
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
-# define VBOX_SKB_CHECKSUM_HELP(skb) skb_checksum_help(skb)
-#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) */
-# define CHECKSUM_PARTIAL CHECKSUM_HW
+# define VBOX_SKB_CHECKSUM_HELP(skb)        skb_checksum_help(skb)
+#else
+# define CHECKSUM_PARTIAL                   CHECKSUM_HW
 # if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10)
-#  define VBOX_SKB_CHECKSUM_HELP(skb) skb_checksum_help(skb, 0)
-# else /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 10) */
+#  define VBOX_SKB_CHECKSUM_HELP(skb)       skb_checksum_help(skb, 0)
+# else
 #  if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 7)
-#   define VBOX_SKB_CHECKSUM_HELP(skb) skb_checksum_help(&skb, 0)
-#  else /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 7) */
-#   define VBOX_SKB_CHECKSUM_HELP(skb) (!skb_checksum_help(skb))
-#  endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 7) */
-# endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 10) */
-#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) */
+#   define VBOX_SKB_CHECKSUM_HELP(skb)      skb_checksum_help(&skb, 0)
+#  else
+#   define VBOX_SKB_CHECKSUM_HELP(skb)      (!skb_checksum_help(skb))
+#  endif
+# endif
+#endif
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
-# define VBOX_SKB_IS_GSO(skb) skb_is_gso(skb)
-                                        /* No features, very dumb device */
-# define VBOX_SKB_GSO_SEGMENT(skb) skb_gso_segment(skb, 0)
-#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18) */
-# define VBOX_SKB_IS_GSO(skb) false
-# define VBOX_SKB_GSO_SEGMENT(skb) NULL
-#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18) */
+/** Indicates that the linux kernel may send us GSO frames. */
+# define VBOXNETFLT_WITH_GSO  1
+#endif
 
 #ifndef NET_IP_ALIGN
@@ -95,21 +90,10 @@
 #endif
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 12)
-unsigned dev_get_flags(const struct net_device *dev)
-{
-        unsigned flags;
-
-        flags = (dev->flags & ~(IFF_PROMISC |
-                                IFF_ALLMULTI |
-                                IFF_RUNNING)) |
-                (dev->gflags & (IFF_PROMISC |
-                                IFF_ALLMULTI));
-
-        if (netif_running(dev) && netif_carrier_ok(dev))
-                flags |= IFF_RUNNING;
-
-        return flags;
-}
-#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 12) */
+#if 0
+/** Create scatter / gather segments for fragments. When not used, we will
+ *  linearize the socket buffer before creating the internal networking SG. */
+# define VBOXNETFLT_SG_SUPPORT 1
+#endif
+
 
 /*******************************************************************************
@@ -137,4 +121,23 @@
 MODULE_VERSION(VBOX_VERSION_STRING " (" RT_XSTR(INTNETTRUNKIFPORT_VERSION) ")");
 #endif
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 12) && defined(LOG_ENABLED)
+unsigned dev_get_flags(const struct net_device *dev)
+{
+    unsigned flags;
+
+    flags = (dev->flags & ~(IFF_PROMISC |
+                            IFF_ALLMULTI |
+                            IFF_RUNNING)) |
+            (dev->gflags & (IFF_PROMISC |
+                            IFF_ALLMULTI));
+
+    if (netif_running(dev) && netif_carrier_ok(dev))
+        flags |= IFF_RUNNING;
+
+    return flags;
+}
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 12) */
 
 
@@ -486,11 +489,11 @@
         }
         pBuf = pCopy;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
+# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
         Log3(("vboxNetFltLinuxPacketHandler: skb copy len=%u data_len=%u truesize=%u next=%p nr_frags=%u gso_size=%u gso_seqs=%u gso_type=%x frag_list=%p pkt_type=%x\n",
               pBuf->len, pBuf->data_len, pBuf->truesize, pBuf->next, skb_shinfo(pBuf)->nr_frags, skb_shinfo(pBuf)->gso_size, skb_shinfo(pBuf)->gso_segs, skb_shinfo(pBuf)->gso_type, skb_shinfo(pBuf)->frag_list, pBuf->pkt_type));
-#else
+# else
         Log3(("vboxNetFltLinuxPacketHandler: skb copy len=%u data_len=%u truesize=%u next=%p nr_frags=%u tso_size=%u tso_seqs=%u frag_list=%p pkt_type=%x\n",
               pBuf->len, pBuf->data_len, pBuf->truesize, pBuf->next, skb_shinfo(pBuf)->nr_frags, skb_shinfo(pBuf)->tso_size, skb_shinfo(pBuf)->tso_segs, skb_shinfo(pBuf)->frag_list, pBuf->pkt_type));
-#endif
+# endif
     }
 #endif
@@ -501,9 +504,16 @@
     Log4(("vboxNetFltLinuxPacketHandler: scheduled work %p for sk_buff %p\n",
           &pThis->u.s.XmitTask, pBuf));
+
     /* It does not really matter what we return, it is ignored by the kernel. */
     return 0;
 }
 
-static unsigned vboxNetFltLinuxSGSegments(PVBOXNETFLTINS pThis, struct sk_buff *pBuf)
+/**
+ * Calculate the number of INTNETSEG segments the socket buffer will need.
+ *
+ * @returns Segment count.
+ * @param   pBuf                The socket buffer.
+ */
+DECLINLINE(unsigned) vboxNetFltLinuxCalcSGSegments(struct sk_buff *pBuf)
 {
 #ifdef VBOXNETFLT_SG_SUPPORT
@@ -513,7 +523,5 @@
 #endif
 #ifdef PADD_RUNT_FRAMES_FROM_HOST
-    /*
-     * Add a trailer if the frame is too small.
-     */
+    /* vboxNetFltLinuxSkBufToSG adds a padding segment if it's a runt. */
     if (pBuf->len < 60)
         cSegs++;
@@ -522,5 +530,8 @@
 }
 
-/* WARNING! This function should only be called after vboxNetFltLinuxSkBufToSG()! */
+/**
+ * Destroy the intnet scatter / gather buffer created by
+ * vboxNetFltLinuxSkBufToSG and free the associated socket buffer.
+ */
 static void  vboxNetFltLinuxFreeSkBuff(struct sk_buff *pBuf, PINTNETSG pSG)
 {
@@ -566,10 +577,17 @@
 #endif
 
+/**
+ * Worker for vboxNetFltLinuxForwardToIntNet.
+ *
+ * @returns VINF_SUCCESS or VERR_NO_MEMORY.
+ * @param   pThis               The net filter instance.
+ * @param   pBuf                The socket buffer.
+ * @param   fSrc                The source.
+ */
 static int vboxNetFltLinuxForwardSegment(PVBOXNETFLTINS pThis, struct sk_buff *pBuf, uint32_t fSrc)
 {
-    unsigned cSegs = vboxNetFltLinuxSGSegments(pThis, pBuf);
-    if (cSegs < MAX_SKB_FRAGS)
-    {
-        uint8_t *pTmp;
+    unsigned cSegs = vboxNetFltLinuxCalcSGSegments(pBuf);
+    if (cSegs < MAX_SKB_FRAGS + 1)
+    {
         PINTNETSG pSG = (PINTNETSG)alloca(RT_OFFSETOF(INTNETSG, aSegs[cSegs]));
         if (!pSG)
@@ -580,5 +598,4 @@
         vboxNetFltLinuxSkBufToSG(pThis, pBuf, pSG, cSegs, fSrc);
 
-        pTmp = pSG->aSegs[0].pv;
         vboxNetFltDumpPacket(pSG, false, (fSrc & INTNETTRUNKDIR_HOST) ? "host" : "wire", 1);
         pThis->pSwitchPort->pfnRecv(pThis->pSwitchPort, pSG, fSrc);
@@ -594,33 +611,40 @@
     uint32_t fSrc = pBuf->pkt_type == PACKET_OUTGOING ? INTNETTRUNKDIR_HOST : INTNETTRUNKDIR_WIRE;
 
-    if (VBOX_SKB_IS_GSO(pBuf))
-    {
-        /* Need to segment the packet */
-        struct sk_buff *pNext, *pSegment;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
-    Log3(("vboxNetFltLinuxForwardToIntNet: skb len=%u data_len=%u truesize=%u next=%p nr_frags=%u gso_size=%u gso_seqs=%u gso_type=%x frag_list=%p pkt_type=%x ip_summed=%d\n",
-          pBuf->len, pBuf->data_len, pBuf->truesize, pBuf->next, skb_shinfo(pBuf)->nr_frags, skb_shinfo(pBuf)->gso_size, skb_shinfo(pBuf)->gso_segs, skb_shinfo(pBuf)->gso_type, skb_shinfo(pBuf)->frag_list, pBuf->pkt_type, pBuf->ip_summed));
-#endif
-
-        pSegment = VBOX_SKB_GSO_SEGMENT(pBuf);
-        if (IS_ERR(pSegment))
+#ifdef VBOXNETFLT_WITH_GSO
+    if (skb_is_gso(pBuf))
+    {
+        Log3(("vboxNetFltLinuxForwardToIntNet: skb len=%u data_len=%u truesize=%u next=%p nr_frags=%u gso_size=%u gso_seqs=%u gso_type=%x frag_list=%p pkt_type=%x ip_summed=%d\n",
+              pBuf->len, pBuf->data_len, pBuf->truesize, pBuf->next, skb_shinfo(pBuf)->nr_frags, skb_shinfo(pBuf)->gso_size, skb_shinfo(pBuf)->gso_segs, skb_shinfo(pBuf)->gso_type, skb_shinfo(pBuf)->frag_list, pBuf->pkt_type, pBuf->ip_summed));
+# if 0 /** @todo receive -> GSO SGs. */
+        if ()
         {
+            Log3(("vboxNetFltLinuxForwardToIntNet: GSO SG\n"));
+            vboxNetFltLinuxForwardGso(pThis, pBuf, fSrc);
+        }
+        else
+# endif
+        {
+            /* Need to segment the packet */
+            struct sk_buff *pNext;
+            struct sk_buff *pSegment = skb_gso_segment(pBuf, 0 /*supported features*/);
+            if (IS_ERR(pSegment))
+            {
+                dev_kfree_skb(pBuf);
+                LogRel(("VBoxNetFlt: Failed to segment a packet (%d).\n", PTR_ERR(pSegment)));
+                return;
+            }
+            for (; pSegment; pSegment = pNext)
+            {
+                Log3(("vboxNetFltLinuxForwardToIntNet: segment len=%u data_len=%u truesize=%u next=%p nr_frags=%u gso_size=%u gso_seqs=%u gso_type=%x frag_list=%p pkt_type=%x\n",
+                      pSegment->len, pSegment->data_len, pSegment->truesize, pSegment->next, skb_shinfo(pSegment)->nr_frags, skb_shinfo(pSegment)->gso_size, skb_shinfo(pSegment)->gso_segs, skb_shinfo(pSegment)->gso_type, skb_shinfo(pSegment)->frag_list, pSegment->pkt_type));
+                pNext = pSegment->next;
+                pSegment->next = 0;
+                vboxNetFltLinuxForwardSegment(pThis, pSegment, fSrc);
+            }
             dev_kfree_skb(pBuf);
-            LogRel(("VBoxNetFlt: Failed to segment a packet (%d).\n", PTR_ERR(pBuf)));
-            return;
         }
-        for (; pSegment; pSegment = pNext)
-        {
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
-            Log3(("vboxNetFltLinuxForwardToIntNet: segment len=%u data_len=%u truesize=%u next=%p nr_frags=%u gso_size=%u gso_seqs=%u gso_type=%x frag_list=%p pkt_type=%x\n",
-                  pSegment->len, pSegment->data_len, pSegment->truesize, pSegment->next, skb_shinfo(pSegment)->nr_frags, skb_shinfo(pSegment)->gso_size, skb_shinfo(pSegment)->gso_segs, skb_shinfo(pSegment)->gso_type, skb_shinfo(pSegment)->frag_list, pSegment->pkt_type));
-#endif
-            pNext = pSegment->next;
-            pSegment->next = 0;
-            vboxNetFltLinuxForwardSegment(pThis, pSegment, fSrc);
-        }
-        dev_kfree_skb(pBuf);
     }
     else
+#endif /* VBOXNETFLT_WITH_GSO */
     {
         if (pBuf->ip_summed == CHECKSUM_PARTIAL && pBuf->pkt_type == PACKET_OUTGOING)
@@ -650,9 +674,12 @@
         vboxNetFltLinuxForwardSegment(pThis, pBuf, fSrc);
     }
-    /*
-     * Create a (scatter/)gather list for the sk_buff and feed it to the internal network.
-     */
-}
-
+}
+
+/**
+ * Work queue handler that forwards the socket buffers queued by
+ * vboxNetFltLinuxPacketHandler to the internal network.
+ *
+ * @param   pWork               The work queue.
+ */
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)
 static void vboxNetFltLinuxXmitTask(struct work_struct *pWork)
@@ -661,26 +688,33 @@
 #endif
 {
+    PVBOXNETFLTINS  pThis   = VBOX_FLT_XT_TO_INST(pWork);
+    RTSPINLOCKTMP   Tmp     = RTSPINLOCKTMP_INITIALIZER;
     struct sk_buff *pBuf;
-    bool fActive;
-    PVBOXNETFLTINS pThis;
-    RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
 
     Log4(("vboxNetFltLinuxXmitTask: Got work %p.\n", pWork));
-    pThis = VBOX_FLT_XT_TO_INST(pWork);
+
     /*
      * Active? Retain the instance and increment the busy counter.
      */
     RTSpinlockAcquire(pThis->hSpinlock, &Tmp);
-    fActive = ASMAtomicUoReadBool(&pThis->fActive);
-    if (fActive)
+    if (ASMAtomicUoReadBool(&pThis->fActive))
+    {
         vboxNetFltRetain(pThis, true /* fBusy */);
-    RTSpinlockRelease(pThis->hSpinlock, &Tmp);
-    if (!fActive)
-        return;
-
-    while ((pBuf = skb_dequeue(&pThis->u.s.XmitQueue)) != 0)
-        vboxNetFltLinuxForwardToIntNet(pThis, pBuf);
-
-    vboxNetFltRelease(pThis, true /* fBusy */);
+        RTSpinlockRelease(pThis->hSpinlock, &Tmp);
+
+        while ((pBuf = skb_dequeue(&pThis->u.s.XmitQueue)) != NULL)
+            vboxNetFltLinuxForwardToIntNet(pThis, pBuf);
+
+        vboxNetFltRelease(pThis, true /* fBusy */);
+    }
+    else
+    {
+        RTSpinlockRelease(pThis->hSpinlock, &Tmp);
+        /** @todo Shouldn't we just drop the packets here? There is little point in
+         *        making them accumulate when the VM is paused and it'll only waste
+         *        kernel memory anyway... Hmm. maybe wait a short while (2-5 secs)
+         *        before start draining the packets (goes for the intnet ring buf
+         *        too)? */
+    }
 }
 
@@ -777,8 +811,8 @@
         dev_set_promiscuity(pDev, 1);
         ASMAtomicWriteBool(&pThis->u.s.fPromiscuousSet, true);
-        Log(("vboxNetFltLinuxDeviceIsUp: enabled promiscuous mode on %s (%d)\n", pThis->szName, VBOX_GET_PCOUNT(pDev)));
+        Log(("vboxNetFltLinuxDeviceIsUp: enabled promiscuous mode on %s (%d)\n", pThis->szName, pDev->promiscuity));
     }
     else
-        Log(("vboxNetFltLinuxDeviceIsUp: no need to enable promiscuous mode on %s (%d)\n", pThis->szName, VBOX_GET_PCOUNT(pDev)));
+        Log(("vboxNetFltLinuxDeviceIsUp: no need to enable promiscuous mode on %s (%d)\n", pThis->szName, pDev->promiscuity));
     return NOTIFY_OK;
 }
@@ -792,8 +826,8 @@
         dev_set_promiscuity(pDev, -1);
         ASMAtomicWriteBool(&pThis->u.s.fPromiscuousSet, false);
-        Log(("vboxNetFltLinuxDeviceGoingDown: disabled promiscuous mode on %s (%d)\n", pThis->szName, VBOX_GET_PCOUNT(pDev)));
+        Log(("vboxNetFltLinuxDeviceGoingDown: disabled promiscuous mode on %s (%d)\n", pThis->szName, pDev->promiscuity));
     }
     else
-        Log(("vboxNetFltLinuxDeviceGoingDown: no need to disable promiscuous mode on %s (%d)\n", pThis->szName, VBOX_GET_PCOUNT(pDev)));
+        Log(("vboxNetFltLinuxDeviceGoingDown: no need to disable promiscuous mode on %s (%d)\n", pThis->szName, pDev->promiscuity));
     return NOTIFY_OK;
 }
@@ -968,5 +1002,5 @@
 #ifdef LOG_ENABLED
         u_int16_t fIf;
-        unsigned const cPromiscBefore = VBOX_GET_PCOUNT(pDev);
+        unsigned const cPromiscBefore = pDev->promiscuity;
 #endif
         if (fActive)
@@ -978,5 +1012,5 @@
             rtnl_unlock();
             pThis->u.s.fPromiscuousSet = true;
-            Log(("vboxNetFltPortOsSetActive: enabled promiscuous mode on %s (%d)\n", pThis->szName, VBOX_GET_PCOUNT(pDev)));
+            Log(("vboxNetFltPortOsSetActive: enabled promiscuous mode on %s (%d)\n", pThis->szName, pDev->promiscuity));
         }
         else
@@ -987,5 +1021,5 @@
                 dev_set_promiscuity(pDev, -1);
                 rtnl_unlock();
-                Log(("vboxNetFltPortOsSetActive: disabled promiscuous mode on %s (%d)\n", pThis->szName, VBOX_GET_PCOUNT(pDev)));
+                Log(("vboxNetFltPortOsSetActive: disabled promiscuous mode on %s (%d)\n", pThis->szName, pDev->promiscuity));
             }
             pThis->u.s.fPromiscuousSet = false;
@@ -993,5 +1027,5 @@
 #ifdef LOG_ENABLED
             fIf = dev_get_flags(pDev);
-            Log(("VBoxNetFlt: fIf=%#x; %d->%d\n", fIf, cPromiscBefore, VBOX_GET_PCOUNT(pDev)));
+            Log(("VBoxNetFlt: fIf=%#x; %d->%d\n", fIf, cPromiscBefore, pDev->promiscuity));
 #endif
         }
