These changes are the raw update to qemu-2.6.
[kvmfornfv.git] / qemu / roms / ipxe / src / drivers / net / ipoib.c
index 1b53917..6552d76 100644 (file)
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
  */
 
-FILE_LICENCE ( GPL2_OR_LATER );
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 
 #include <stdint.h>
 #include <stdlib.h>
@@ -29,8 +33,10 @@ FILE_LICENCE ( GPL2_OR_LATER );
 #include <ipxe/errortab.h>
 #include <ipxe/malloc.h>
 #include <ipxe/if_arp.h>
+#include <ipxe/arp.h>
 #include <ipxe/if_ether.h>
 #include <ipxe/ethernet.h>
+#include <ipxe/ip.h>
 #include <ipxe/iobuf.h>
 #include <ipxe/netdevice.h>
 #include <ipxe/infiniband.h>
@@ -44,6 +50,20 @@ FILE_LICENCE ( GPL2_OR_LATER );
  * IP over Infiniband
  */
 
+/* Disambiguate the various error causes */
+#define ENXIO_ARP_REPLY __einfo_error ( EINFO_ENXIO_ARP_REPLY )
+#define EINFO_ENXIO_ARP_REPLY                                          \
+       __einfo_uniqify ( EINFO_ENXIO, 0x01,                            \
+                         "Missing REMAC for ARP reply target address" )
+#define ENXIO_NON_IPV4 __einfo_error ( EINFO_ENXIO_NON_IPV4 )
+#define EINFO_ENXIO_NON_IPV4                                           \
+       __einfo_uniqify ( EINFO_ENXIO, 0x02,                            \
+                         "Missing REMAC for non-IPv4 packet" )
+#define ENXIO_ARP_SENT __einfo_error ( EINFO_ENXIO_ARP_SENT )
+#define EINFO_ENXIO_ARP_SENT                                           \
+       __einfo_uniqify ( EINFO_ENXIO, 0x03,                            \
+                         "Missing REMAC for IPv4 packet (ARP sent)" )
+
 /** Number of IPoIB send work queue entries */
 #define IPOIB_NUM_SEND_WQES 2
 
@@ -96,6 +116,8 @@ struct errortab ipoib_errors[] __errortab = {
        __einfo_errortab ( EINFO_EINPROGRESS_JOINING ),
 };
 
+static struct net_device_operations ipoib_operations;
+
 /****************************************************************************
  *
  * IPoIB REMAC cache
@@ -124,8 +146,10 @@ static struct ipoib_mac * ipoib_find_remac ( struct ipoib_device *ipoib,
                                             const struct ipoib_remac *remac ) {
        struct ipoib_peer *peer;
 
-       /* Check for broadcast REMAC */
-       if ( is_broadcast_ether_addr ( remac ) )
+       /* Check for broadcast or multicast REMAC.  We transmit
+        * multicasts as broadcasts for simplicity.
+        */
+       if ( is_multicast_ether_addr ( remac ) )
                return &ipoib->broadcast;
 
        /* Try to find via REMAC cache */
@@ -202,14 +226,20 @@ static void ipoib_flush_remac ( struct ipoib_device *ipoib ) {
  * @ret discarded      Number of cached items discarded
  */
 static unsigned int ipoib_discard_remac ( void ) {
-       struct ib_device *ibdev;
+       struct net_device *netdev;
        struct ipoib_device *ipoib;
        struct ipoib_peer *peer;
        unsigned int discarded = 0;
 
        /* Try to discard one cache entry for each IPoIB device */
-       for_each_ibdev ( ibdev ) {
-               ipoib = ib_get_ownerdata ( ibdev );
+       for_each_netdev ( netdev ) {
+
+               /* Skip non-IPoIB devices */
+               if ( netdev->op != &ipoib_operations )
+                       continue;
+               ipoib = netdev->priv;
+
+               /* Discard least recently used cache entry (if any) */
                list_for_each_entry_reverse ( peer, &ipoib->peers, list ) {
                        list_del ( &peer->list );
                        free ( peer );
@@ -222,7 +252,7 @@ static unsigned int ipoib_discard_remac ( void ) {
 }
 
 /** IPoIB cache discarder */
-struct cache_discarder ipoib_discarder __cache_discarder ( CACHE_NORMAL ) = {
+struct cache_discarder ipoib_discarder __cache_discarder ( CACHE_EXPENSIVE ) = {
        .discard = ipoib_discard_remac,
 };
 
@@ -324,8 +354,11 @@ static int ipoib_translate_tx_arp ( struct net_device *netdev,
        /* Look up REMAC, if applicable */
        if ( arphdr->ar_op == ARPOP_REPLY ) {
                target_ha = ipoib_find_remac ( ipoib, arp_target_pa ( arphdr ));
-               if ( ! target_ha )
-                       return -ENXIO;
+               if ( ! target_ha ) {
+                       DBGC ( ipoib, "IPoIB %p no REMAC for %s ARP reply\n",
+                              ipoib, eth_ntoa ( arp_target_pa ( arphdr ) ) );
+                       return -ENXIO_ARP_REPLY;
+               }
        }
 
        /* Construct new packet */
@@ -461,6 +494,7 @@ static int ipoib_transmit ( struct net_device *netdev,
        struct ipoib_device *ipoib = netdev->priv;
        struct ib_device *ibdev = ipoib->ibdev;
        struct ethhdr *ethhdr;
+       struct iphdr *iphdr;
        struct ipoib_hdr *ipoib_hdr;
        struct ipoib_mac *mac;
        struct ib_address_vector dest;
@@ -485,9 +519,34 @@ static int ipoib_transmit ( struct net_device *netdev,
        iob_pull ( iobuf, sizeof ( *ethhdr ) );
 
        /* Identify destination address */
-       mac = ipoib_find_remac ( ipoib, ( ( void *) ethhdr->h_dest ) );
-       if ( ! mac )
-               return -ENXIO;
+       mac = ipoib_find_remac ( ipoib, ( ( void * ) ethhdr->h_dest ) );
+       if ( ! mac ) {
+               /* Generate a new ARP request (if possible) to trigger
+                * population of the REMAC cache entry.
+                */
+               if ( ( net_proto != htons ( ETH_P_IP ) ) ||
+                    ( iob_len ( iobuf ) < sizeof ( *iphdr ) ) ) {
+                       DBGC ( ipoib, "IPoIB %p no REMAC for %s non-IPv4 "
+                              "packet type %04x\n", ipoib,
+                              eth_ntoa ( ethhdr->h_dest ),
+                              ntohs ( net_proto ) );
+                       return -ENXIO_NON_IPV4;
+               }
+               iphdr = iobuf->data;
+               if ( ( rc = arp_tx_request ( netdev, &ipv4_protocol,
+                                            &iphdr->dest, &iphdr->src ) ) !=0){
+                       DBGC ( ipoib, "IPoIB %p could not ARP for %s/%s/",
+                              ipoib, eth_ntoa ( ethhdr->h_dest ),
+                              inet_ntoa ( iphdr->dest ) );
+                       DBGC ( ipoib, "%s: %s\n", inet_ntoa ( iphdr->src ),
+                              strerror ( rc ) );
+                       return rc;
+               }
+               DBGC ( ipoib, "IPoIB %p no REMAC for %s/%s/", ipoib,
+                      eth_ntoa ( ethhdr->h_dest ), inet_ntoa ( iphdr->dest ) );
+               DBGC  ( ipoib, "%s\n", inet_ntoa ( iphdr->src ) );
+               return -ENXIO_ARP_SENT;
+       }
 
        /* Translate packet if applicable */
        if ( ( rc = ipoib_translate_tx ( netdev, iobuf, net_proto ) ) != 0 )
@@ -732,7 +791,8 @@ static void ipoib_link_state_changed ( struct ib_device *ibdev ) {
        int rc;
 
        /* Leave existing broadcast group */
-       ipoib_leave_broadcast_group ( ipoib );
+       if ( ipoib->qp )
+               ipoib_leave_broadcast_group ( ipoib );
 
        /* Update MAC address based on potentially-new GID prefix */
        memcpy ( &ipoib->mac.gid.s.prefix, &ibdev->gid.s.prefix,
@@ -747,7 +807,7 @@ static void ipoib_link_state_changed ( struct ib_device *ibdev ) {
        netdev_link_err ( netdev, ( rc ? rc : -EINPROGRESS_JOINING ) );
 
        /* Join new broadcast group */
-       if ( ib_is_open ( ibdev ) && ib_link_ok ( ibdev ) &&
+       if ( ib_is_open ( ibdev ) && ib_link_ok ( ibdev ) && ipoib->qp &&
             ( ( rc = ipoib_join_broadcast_group ( ipoib ) ) != 0 ) ) {
                DBGC ( ipoib, "IPoIB %p could not rejoin broadcast group: "
                       "%s\n", ipoib, strerror ( rc ) );
@@ -835,7 +895,9 @@ static void ipoib_close ( struct net_device *netdev ) {
 
        /* Tear down the queues */
        ib_destroy_qp ( ibdev, ipoib->qp );
+       ipoib->qp = NULL;
        ib_destroy_cq ( ibdev, ipoib->cq );
+       ipoib->cq = NULL;
 
        /* Close IB device */
        ib_close ( ibdev );