Assume natural alignment for IP & ether addrs
authorAndrew Gallatin <gallatin@google.com>
Mon, 23 Jun 2014 20:20:35 +0000 (13:20 -0700)
committerAndrew Gallatin <gallatin@google.com>
Mon, 23 Jun 2014 23:19:40 +0000 (16:19 -0700)
Assume natural alignment for IP addresses and ethernet addresses in
the critical path.  By doing so, we can compare and copy via direct
4-byte load / store rather than either calling memcmp()/memmove(), or
doing hand-unrolled byte-by-byte operations.

This gains about 750Mb/s on a netperf TCP receive workload with a
10GbE NIC in a low-end x86_64.

Signed-off-by: Andrew Gallatin <gallatin@google.com>
kern/drivers/dev/ether.c
kern/include/ip.h
kern/src/net/arp.c
kern/src/net/ethermedium.c
kern/src/net/ipaux.c
kern/src/net/ipifc.c

index 37cd276..dbdc8e0 100644 (file)
@@ -251,6 +251,18 @@ static void etherrtrace(struct netfile *f, struct etherpkt *pkt, int len)
        qpass(f->in, bp);
 }
 
+#ifdef CONFIG_RISCV
+#warning "Potentially unaligned ethernet addrs!"
+#endif
+
+static inline int eaddrcmp(uint8_t *x, uint8_t *y)
+{
+       uint16_t *a = (uint16_t *)x;
+       uint16_t *b = (uint16_t *)y;
+
+       return (a[0] ^ b[0]) | (a[1] ^ b[1]) | (a[2] ^ b[2]);
+}
+
 struct block *etheriq(struct ether *ether, struct block *bp, int fromwire)
 {
        struct etherpkt *pkt;
@@ -285,7 +297,7 @@ struct block *etheriq(struct ether *ether, struct block *bp, int fromwire)
 
        multi = pkt->d[0] & 1;
        /* check for valid multcast addresses */
-       if (multi && memcmp(pkt->d, ether->netif.bcast, sizeof(pkt->d)) != 0
+       if (multi && eaddrcmp(pkt->d, ether->netif.bcast) != 0
                && ether->netif.prom == 0) {
                if (!activemulti(&ether->netif, pkt->d, sizeof(pkt->d))) {
                        if (fromwire) {
@@ -297,8 +309,8 @@ struct block *etheriq(struct ether *ether, struct block *bp, int fromwire)
        }
 
        /* is it for me? */
-       tome = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;
-       fromme = memcmp(pkt->s, ether->ea, sizeof(pkt->s)) == 0;
+       tome = eaddrcmp(pkt->d, ether->ea) == 0;
+       fromme = eaddrcmp(pkt->s, ether->ea) == 0;
 
        /*
         * Multiplex the packet to all the connections which want it.
@@ -359,8 +371,8 @@ static int etheroq(struct ether *ether, struct block *bp)
         */
        pkt = (struct etherpkt *)bp->rp;
        len = BLEN(bp);
-       loopback = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;
-       if (loopback || memcmp(pkt->d, ether->netif.bcast, sizeof(pkt->d)) == 0
+       loopback = eaddrcmp(pkt->d, ether->ea) == 0;
+       if (loopback || eaddrcmp(pkt->d, ether->netif.bcast) == 0
                || ether->netif.prom) {
                disable_irqsave(&irq_state);
                etheriq(ether, bp, 0);
index 2bf7887..2cb2c35 100644 (file)
@@ -531,8 +531,29 @@ extern void v4tov6(uint8_t * v6, uint8_t * v4);
 extern int v6tov4(uint8_t * v4, uint8_t * v6);
 //extern int    eipfmt(Fmt*);
 
-#define        ipmove(x, y) memmove(x, y, IPaddrlen)
-#define        ipcmp(x, y) ( (x)[IPaddrlen-1] != (y)[IPaddrlen-1] || memcmp(x, y, IPaddrlen) )
+
+#ifdef CONFIG_RISCV
+#warning "Potentially unaligned IP addrs!"
+#endif
+static inline void ipmove(unsigned char *x, unsigned char *y)
+{
+       uint32_t *a = (uint32_t *)x;
+       uint32_t *b = (uint32_t *)y;
+
+       a[0] = b[0];
+       a[1] = b[1];
+       a[2] = b[2];
+       a[3] = b[3];
+}
+
+static inline long ipcmp(unsigned char *x, unsigned char *y)
+{
+       uint32_t *a = (uint32_t *)x;
+       uint32_t *b = (uint32_t *)y;
+       return (a[0] ^ b[0]) | (a[1] ^ b[1]) |
+               (a[2] ^ b[2]) | (a[3] ^ b[3]);
+}
+
 
 extern uint8_t IPv4bcast[IPaddrlen];
 extern uint8_t IPv4bcastobs[IPaddrlen];
index 0f45fa8..00e07b0 100644 (file)
@@ -206,10 +206,11 @@ void cleanarpent(struct arp *arp, struct arpent *a)
 struct arpent *arpget(struct arp *arp, struct block *bp, int version,
                                          struct Ipifc *ifc, uint8_t * ip, uint8_t * mac)
 {
-       int hash;
+       int hash, len;
        struct arpent *a;
        struct medium *type = ifc->m;
        uint8_t v6ip[IPaddrlen];
+       uint16_t *s, *d;
 
        if (version == V4) {
                v4tov6(v6ip, ip);
@@ -219,7 +220,7 @@ struct arpent *arpget(struct arp *arp, struct block *bp, int version,
        qlock(&arp->qlock);
        hash = haship(ip);
        for (a = arp->hash[hash]; a; a = a->hash) {
-               if (memcmp(ip, a->ip, sizeof(a->ip)) == 0)
+               if (ipcmp(ip, a->ip) == 0)
                        if (type == a->type)
                                break;
        }
@@ -241,7 +242,13 @@ struct arpent *arpget(struct arp *arp, struct block *bp, int version,
                return a;       /* return with arp qlocked */
        }
 
-       memmove(mac, a->mac, a->type->maclen);
+       s = (uint16_t *)a->mac;
+       d = (uint16_t *)mac;
+       len = a->type->maclen / 2;
+       while (len) {
+               *d++ = *s++;
+               len--;
+       }
 
        /* remove old entries */
        if (NOW - a->ctime > 15 * 60 * 1000)
index 5d654cf..15ae69b 100644 (file)
@@ -288,6 +288,19 @@ static void etherunbind(struct Ipifc *ifc)
 }
 
 /*
+ * copy ethernet address
+ */
+static inline void etherfilladdr(uint16_t *pkt, uint16_t *dst, uint16_t *src)
+{
+       *pkt++ = *dst++;
+       *pkt++ = *dst++;
+       *pkt++ = *dst++;
+       *pkt++ = *src++;
+       *pkt++ = *src++;
+       *pkt = *src;
+}
+
+/*
  *  called by ipoput with a single block to write with ifc rlock'd
  */
 static void
@@ -325,8 +338,7 @@ etherbwrite(struct Ipifc *ifc, struct block *bp, int version, uint8_t * ip)
        eh = (Etherhdr *) bp->rp;
 
        /* copy in mac addresses and ether type */
-       memmove(eh->s, ifc->mac, sizeof(eh->s));
-       memmove(eh->d, mac, sizeof(eh->d));
+       etherfilladdr((uint16_t *)bp->rp, (uint16_t *)mac, (uint16_t *)ifc->mac);
 
        switch (version) {
                case V4:
index 0c6ca02..5ac4872 100644 (file)
@@ -12,6 +12,7 @@
 #include <pmap.h>
 #include <smp.h>
 #include <ip.h>
+#include <endian.h>
 
 /*
  *  well known IP addresses
@@ -324,51 +325,37 @@ extern char *v4parseip(uint8_t * to, char *from)
 
 int isv4(uint8_t * ip)
 {
-       return memcmp(ip, v4prefix, IPv4off) == 0;
+       unsigned short *ips = (unsigned short *)ip;
+       return 0xffff == ips[5];
 }
 
 /*
  *  the following routines are unrolled with no memset's to speed
- *  up the usual case
+ *  up the usual case.  They assume IP addresses are naturally aligned.
  */
 void v4tov6(uint8_t * v6, uint8_t * v4)
 {
-       v6[0] = 0;
-       v6[1] = 0;
-       v6[2] = 0;
-       v6[3] = 0;
-       v6[4] = 0;
-       v6[5] = 0;
-       v6[6] = 0;
-       v6[7] = 0;
-       v6[8] = 0;
-       v6[9] = 0;
-       v6[10] = 0xff;
-       v6[11] = 0xff;
-       v6[12] = v4[0];
-       v6[13] = v4[1];
-       v6[14] = v4[2];
-       v6[15] = v4[3];
+       uint32_t *v6p = (uint32_t *)v6;
+       uint32_t *v4p = (uint32_t *)v4;
+
+       v6p[0] = 0;
+       v6p[1] = 0;
+       v6p[2] = (unsigned int)PP_NTOHL(0xffff);
+       v6p[3] = v4p[0];
 }
 
 int v6tov4(uint8_t * v4, uint8_t * v6)
 {
-       if (v6[0] == 0
-               && v6[1] == 0
-               && v6[2] == 0
-               && v6[3] == 0
-               && v6[4] == 0
-               && v6[5] == 0
-               && v6[6] == 0
-               && v6[7] == 0
-               && v6[8] == 0 && v6[9] == 0 && v6[10] == 0xff && v6[11] == 0xff) {
-               v4[0] = v6[12];
-               v4[1] = v6[13];
-               v4[2] = v6[14];
-               v4[3] = v6[15];
+
+       uint32_t *v6p = (uint32_t *)v6;
+       uint32_t *v4p = (uint32_t *)v4;
+
+       if (v6p[0] == 0 && v6p[1] == 0 &&
+           v6p[2] == (unsigned int)PP_NTOHL(0xffff)) {
+               v4p[0] = v6p[3];
                return 0;
        } else {
-               memset(v4, 0, 4);
+               v4p[0] = 0;
                return -1;
        }
 }
index ec4176a..428b7d2 100644 (file)
@@ -1258,7 +1258,7 @@ void findlocalip(struct Fs *f, uint8_t * local, uint8_t * remote)
 
        qlock(&f->ipifc->qlock);
        r = v6lookup(f, remote, NULL);
-       version = (memcmp(remote, v4prefix, IPv4off) == 0) ? V4 : V6;
+       version = isv4(remote) ? V4 : V6;
 
        if (r != NULL) {
                ifc = r->rt.ifc;