From: Justin Pettit <jpettit@nicira.com>
Date: Fri, 26 Sep 2008 21:58:17 +0000 (-0700)
Subject: Remove kernel datapath unit tests.
X-Git-Url: http://git.onelab.eu/?a=commitdiff_plain;h=0fab7c69aedde080f3181341f154ac4c44d7bc5d;p=sliver-openvswitch.git

Remove kernel datapath unit tests.

The tests haven't been seeing much love and are suffering from bit-rot at
this point.  We're removing them, since we have other ways to test at
this point.
---

diff --git a/configure.ac b/configure.ac
index 69e00470f..aa1ce99f4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -72,7 +72,6 @@ utilities/Makefile
 secchan/Makefile
 switch/Makefile
 tests/Makefile
-datapath/tests/Makefile
 third-party/Makefile
 debian/Makefile
 datapath/linux-2.6/Kbuild
diff --git a/datapath/Makefile.am b/datapath/Makefile.am
index 978553787..88e66c745 100644
--- a/datapath/Makefile.am
+++ b/datapath/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = tests
+SUBDIRS = 
 if L26_ENABLED
 SUBDIRS += linux-2.6
 endif
diff --git a/datapath/Modules.mk b/datapath/Modules.mk
index f21af16b4..516ebd9df 100644
--- a/datapath/Modules.mk
+++ b/datapath/Modules.mk
@@ -1,5 +1,5 @@
 all_modules = $(dist_modules)
-dist_modules = openflow unit
+dist_modules = openflow 
 
 openflow_sources = \
 	chain.c \
@@ -10,8 +10,7 @@ openflow_sources = \
 	flow.c \
 	forward.c \
 	table-hash.c \
-	table-linear.c \
-	unit-exports.c
+	table-linear.c 
 
 openflow_headers = \
 	chain.h \
@@ -22,14 +21,7 @@ openflow_headers = \
 	flow.h \
 	forward.h \
 	snap.h \
-	table.h \
-	unit.h
-
-unit_sources = \
-	crc_t.c \
-	forward_t.c \
-	table_t.c \
-	unit.c
+	table.h 
 
 dist_sources = $(foreach module,$(dist_modules),$($(module)_sources))
 dist_headers = $(foreach module,$(dist_modules),$($(module)_headers))
diff --git a/datapath/crc_t.c b/datapath/crc_t.c
deleted file mode 100644
index 1e8274aa5..000000000
--- a/datapath/crc_t.c
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Distributed under the terms of the GNU GPL version 2.
- * Copyright (c) 2007, 2008 The Board of Trustees of The Leland 
- * Stanford Junior University
- */
-
-#include <linux/string.h>
-#include <linux/kernel.h>
-
-#include "crc32.h"
-#include "unit.h"
-
-
-static void
-print_error(unsigned int poly, char *data,
-			unsigned int expected, unsigned int calculated)
-{
-	unit_fail("crc error: poly=%x data=%s expected=%x calculated=%x\n",
-				poly, data, expected, calculated);
-}
-
-void
-run_crc_t(void)
-{
-	struct crc32 crc;
-	unsigned int val, i, j;
-
-	char *data[3] = { "h3rei$@neX@mp13da7@sTr117G0fCH@r$",
-				"1324lkqasdf0-[LKJD0;asd,.cv;/asd0:\"'~`co29",
-				"6" };
-
-	unsigned int polys[2] = { 0x04C11DB7,
-				0x1EDC6F41 };
-
-	unsigned int crc_values[2][3] = { 
-				{ 0xDE1040C3, 0x65343A0B, 0xCEB42022 },
-				{ 0x6C149FAE, 0x470A6B73, 0x4D3AA134 } };
-	for (i = 0; i < 2; i++) {
-		crc32_init(&crc, polys[i]);
-		for (j = 0; j < 3; j++) {
-			val = crc32_calculate(&crc, data[j], strlen(data[j]));
-			if (val != crc_values[i][j]) {
-				print_error(polys[i], data[j], crc_values[i][j], val);
-			}
-		}
-	}
-}
diff --git a/datapath/forward_t.c b/datapath/forward_t.c
deleted file mode 100644
index da5de50c8..000000000
--- a/datapath/forward_t.c
+++ /dev/null
@@ -1,584 +0,0 @@
-/*
- * Distributed under the terms of the GNU GPL version 2.
- * Copyright (c) 2007, 2008 The Board of Trustees of The Leland 
- * Stanford Junior University
- */
-
-#include <linux/skbuff.h>
-#include <linux/if_ether.h>
-#include <linux/if_vlan.h>
-#include <linux/in.h>
-#include <linux/ip.h>
-#include <linux/random.h>
-#include <linux/tcp.h>
-#include <linux/udp.h>
-
-#include "forward.h"
-#include "tests/forward_t.h"
-#include "openflow.h"
-#include "unit.h"
-#include "flow.h"
-
-/*
- * Tests execute_settings() in forward.c to check that actions are
- * appropriately taken on packets, meaning:
- *
- * 1. Checksums are correct.
- * 2. Actions are only taken on compatible packets (IP action not taken on
- * non-IP packet)
- * 3. Other packet data remains untouched.
-
- * forward_t.h contains static packet definitions.  forward_t.h should be
- * generated using gen_forward_t.c.  This test is run on whatever packets are
- * defined in forward_t.h.
- *
- * NOTE:  Tests assume packets in forward_t.h are present in full and IP and
- * transport checksums are correct. (Can prevent offloading of checksum
- * computation using ethtool.
- */
-
-/*
- * Sets 'a->data'.  If 'key' != NULL, sets 'data' to equal 'key's value for type
- * specified by 'a->type'.  If 'key' == NULL, sets data to a random value.
- */
-
-static void
-set_action_data(struct sk_buff *skb, struct sw_flow_key *key, struct ofp_action *a)
-{
-	if (key != NULL) {
-		switch(ntohs(a->type)) {
-		case(OFPAT_SET_DL_SRC):
-			memcpy(a->arg.dl_addr, key->dl_src, sizeof key->dl_src);
-			break;
-		case(OFPAT_SET_DL_DST):
-			memcpy(a->arg.dl_addr, key->dl_dst, sizeof key->dl_dst);
-			break;
-		case(OFPAT_SET_NW_SRC):
-			if (key->dl_type == htons(ETH_P_IP))
-				a->arg.nw_addr = key->nw_src;
-			else
-				a->arg.nw_addr = random32();
-			break;
-		case(OFPAT_SET_NW_DST):
-			if (key->dl_type == htons(ETH_P_IP))
-				a->arg.nw_addr = key->nw_dst;
-			else
-				a->arg.nw_addr = random32();
-			break;
-		case(OFPAT_SET_TP_SRC):
-			if (key->nw_proto == IPPROTO_TCP || key->nw_proto == IPPROTO_UDP)
-				a->arg.tp = key->tp_src;
-			else
-				a->arg.tp = (uint16_t) random32();
-			break;
-		case(OFPAT_SET_TP_DST):
-			if (key->nw_proto == IPPROTO_TCP || key->nw_proto == IPPROTO_UDP)
-				a->arg.tp = key->tp_dst;
-			else
-				a->arg.tp = (uint16_t) random32();
-			break;
-		default:
-			BUG();
-		}
-	} else {
-		((uint32_t*)a->arg.dl_addr)[0] = random32();
-		((uint16_t*)a->arg.dl_addr)[2] = random32();
-	}
-}
-
-
-/*
- * Checks the IP sum of an IP packet.  Returns 0 if correct, else -1.
- */
-
-static void
-check_IP_csum(struct iphdr *ih)
-{
-	uint16_t check, *data;
-	uint32_t n_bytes, sum;
-
-	check = ih->check;
-	ih->check = 0;
-	data = (uint16_t*) ih;
-	sum = 0;
-	n_bytes = ih->ihl * 4;
-
-	while (n_bytes > 1) {
-		sum += ntohs(*data);
-		sum = (sum >> 16) + (uint16_t)sum;
-		data++;
-		n_bytes -= 2;
-	}
-
-	if (n_bytes == 1) {
-		sum += *(uint8_t*)data;
-		sum = (sum >> 16) + (uint16_t)sum;
-	}
-
-	ih->check = htons((uint16_t)(~sum));
-	if (ih->check != check) {
-		unit_fail("IP checksum %hu does not match %hu",
-			  ntohs(ih->check), ntohs(check));
-	}
-}
-
-/*
- * Partially computes TCP checksum over 'n_bytes' pointed to by 'data'.  Can be
- * called multiple times if data csum is to be computed on is fragmented.  If
- * 'is_last' == 0, assumes will be called again on more data and returns the
- * value that should be passed in as 'incr_sum' on the next call.  Else if
- * 'is_last' == 1, returns the final checksum.  On the first call, 'incr_sum'
- * should equal 0.  If 'is_last' == 0, 'n_bytes' must be even.  i.e. Should
- * first be called on pseudo header fields that are multiples of two, and then
- * on the TCP packet.
- */
-static uint32_t
-compute_transport_checksum(uint16_t *data, uint32_t n_bytes,
-			uint32_t incr_sum, uint8_t is_last)
-{
-	uint8_t arr[2];
-
-	if (n_bytes % 2 != 0 && is_last == 0)
-		BUG();
-
-	while (n_bytes > 1) {
-		incr_sum += ntohs(*data);
-		incr_sum = (incr_sum >> 16) + (uint16_t)incr_sum;
-		data++;
-		n_bytes -= 2;
-	}
-
-	if (is_last == 0)
-		return incr_sum;
-
-	if(n_bytes == 1) {
-		arr[0] = *(uint8_t*)data;
-		arr[1] = 0;
-		incr_sum += ntohs(*((uint16_t*)arr));
-		incr_sum = (incr_sum >> 16) + (uint16_t)incr_sum;
-	}
-
-	return ~incr_sum;
-}
-
-/*
- * Checks the transport layer's checksum of a packet.  Returns '0' if correct,
- * else '1'.  'ih' should point to the IP header of the packet, if TCP, 'th'
- * should point the TCP header, and if UDP, 'uh' should point to the UDP
- * header.
- */
-static int
-check_transport_csum(struct iphdr *ih, struct tcphdr *th,
-			 struct udphdr *uh)
-{
-	uint32_t tmp;
-	uint16_t len, check;
-	uint8_t arr[2];
-
-	tmp = compute_transport_checksum((uint16_t*)(&ih->saddr),
-					 2 * sizeof ih->saddr, 0, 0);
-	arr[0] = 0;
-	arr[1] = ih->protocol;
-	tmp = compute_transport_checksum((uint16_t*)arr, 2, tmp, 0);
-	len = ntohs(ih->tot_len) - (ih->ihl * 4);
-	*((uint16_t*)arr) = htons(len);
-	tmp = compute_transport_checksum((uint16_t*)arr, 2, tmp, 0);
-
-	if (th != NULL) {
-		check = th->check;
-		th->check = 0;
-		th->check = htons((uint16_t)compute_transport_checksum((uint16_t*)th,
-									len, tmp, 1));
-		if (th->check != check) {
-			unit_fail("TCP checksum %hu does not match %hu",
-				  ntohs(th->check), ntohs(check));
-			return -1;
-		}
-	} else if (uh != NULL) {
-		check = uh->check;
-		uh->check = 0;
-		uh->check = htons((uint16_t)compute_transport_checksum((uint16_t*)uh,
-									len, tmp, 1));
-		if (uh->check != check) {
-			unit_fail("UDP checksum %hu does not match %hu",
-				  ntohs(uh->check), ntohs(check));
-			return -1;
-		}
-	}
-
-	return 0;
-}
-
-
-/*
- * Compares 'pkt_len' bytes of 'data' to 'pkt'.  excl_start and excl_end point
- * together delineate areas of 'data' that are not supposed to match 'pkt'.
- * 'num_excl' specify how many such areas exist.  An 'excl_start' entry is
- * ignored if it equals NULL.  See 'check_packet()' for usage.
- */
-
-static void
-compare(uint8_t *data, uint8_t *pkt, uint32_t pkt_len,
-	uint8_t **excl_start, uint8_t **excl_end, uint32_t num_excl)
-{
-	uint32_t i;
-	uint8_t *d, *p, *end;
-	int ret;
-
-	end = data + pkt_len;
-	d = data;
-	p = pkt;
-	ret = 0;
-
-	for (i = 0; i < num_excl; i++) {
-		if(*excl_start != NULL) {
-			if ((ret = memcmp(d, p, *excl_start - d)) != 0)
-				break;
-			p += (*excl_end - d);
-			d = *excl_end;
-		}
-		excl_start++;
-		excl_end++;
-	}
-
-	if (ret == 0)
-		ret = memcmp(d, p, end - d);
-
-	if (ret != 0) {
-		unit_fail("skb and packet comparison failed:");
-		for (i = 0; i < pkt_len; i++) {
-			if (data[i] != pkt[i]) {
-				unit_fail("skb[%u] = 0x%x != 0x%x",
-					  i, data[i], pkt[i]);
-			}
-		}
-	}
-}
-
-
-/*
- * Checks that a packet's data has remained consistent after an action has been
- * applied.  'skb' is the modified packet, 'a' is the action that was taken on
- * the packet, 'p' is a copy of the packet's data before action 'a' was taken.
- * Checks that the action was in fact taken, that the checksums of the packet
- * are correct, and that no other data in the packet was altered.
- */
-
-static void
-check_packet(struct sk_buff *skb, struct ofp_action *a, struct pkt *p)
-{
-	struct ethhdr *eh;
-	struct iphdr *ih;
-	struct tcphdr *th;
-	struct udphdr *uh;
-	uint8_t *excl_start[5], *excl_end[5];
-
-	eh = eth_hdr(skb);
-	ih = NULL;
-	th = NULL;
-	uh = NULL;
-
-	memset(excl_start, 0, sizeof excl_start);
-	memset(excl_end, 0, sizeof excl_end);
-
-	if (eh->h_proto == htons(ETH_P_IP)) {
-		ih = ip_hdr(skb);
-		excl_start[1] = (uint8_t*)&ih->check;
-		excl_end[1] = (uint8_t*)(&ih->check + 1);
-		if (ih->protocol == IPPROTO_TCP) {
-			th = tcp_hdr(skb);
-			excl_start[4] = (uint8_t*)&th->check;
-			excl_end[4] = (uint8_t*)(&th->check + 1);
-		} else if (ih->protocol == IPPROTO_UDP) {
-			uh = udp_hdr(skb);
-			excl_start[4] = (uint8_t*)&uh->check;
-			excl_end[4] = (uint8_t*)(&uh->check + 1);
-		}
-	}
-
-	if (a != NULL) {
-		switch(ntohs(a->type)) {
-		case(OFPAT_SET_DL_SRC):
-			if (memcmp(a->arg.dl_addr, eh->h_source, sizeof eh->h_source) != 0) {
-				unit_fail("Source eth addr has not been set");
-				return;
-			}
-			excl_start[0] = (uint8_t*)(&eh->h_source);
-			excl_end[0] = (uint8_t*)(&eh->h_proto);
-			break;
-		case(OFPAT_SET_DL_DST):
-			if (memcmp(a->arg.dl_addr, eh->h_dest, sizeof eh->h_dest) != 0) {
-				unit_fail("Dest eth addr has not been set");
-				return;
-			}
-			excl_start[0] = (uint8_t*)(&eh->h_dest);
-			excl_end[0] = (uint8_t*)(&eh->h_source);
-			break;
-		case(OFPAT_SET_NW_SRC):
-			if (ih != NULL) {
-				if (a->arg.nw_addr != ih->saddr) {
-					unit_fail("Source IP addr has not been set");
-					return;
-				}
-				excl_start[2] = (uint8_t*)(&ih->saddr);
-				excl_end[2] = (uint8_t*)(&ih->saddr + 1);
-			}
-			break;
-		case(OFPAT_SET_NW_DST):
-			if (ih != NULL) {
-				if (a->arg.nw_addr != ih->daddr) {
-					unit_fail("Dest IP addr has not been set");
-					return;
-				}
-				excl_start[2] = (uint8_t*)(&ih->daddr);
-				excl_end[2] = (uint8_t*)(&ih->daddr + 1);
-			}
-			break;
-		case(OFPAT_SET_TP_SRC):
-			if (th != NULL) {
-				if (a->arg.tp != th->source) {
-					unit_fail("Source port has not been set");
-					return;
-				}
-				excl_start[3] = (uint8_t*)(&th->source);
-				excl_end[3] = (uint8_t*)(&th->source + 1);
-			} else if (uh != NULL) {
-				if (a->arg.tp != uh->source) {
-					unit_fail("Source port has not been set");
-					return;
-				}
-				excl_start[3] = (uint8_t*)(&uh->source);
-				excl_end[3] = (uint8_t*)(&uh->source + 1);
-			}
-			break;
-		case(OFPAT_SET_TP_DST):
-			if (th != NULL) {
-				if (a->arg.tp != th->dest) {
-					unit_fail("Dest port has not been set");
-					return;
-				}
-				excl_start[3] = (uint8_t*)(&th->dest);
-				excl_end[3] = (uint8_t*)(&th->dest + 1);
-			} else if (uh != NULL) {
-				if (a->arg.tp != uh->dest) {
-					unit_fail("Dest port has not been set");
-					return;
-				}
-				excl_start[3] = (uint8_t*)(&uh->dest);
-				excl_end[3] = (uint8_t*)(&uh->dest + 1);
-			}
-			break;
-		default:
-			BUG();
-		}
-	}
-
-	compare(skb->data, p->data, p->len, excl_start, excl_end, 5);
-	if (unit_failed())
-		return;
-
-	if (ih == NULL)
-		return;
-
-	check_IP_csum(ih);
-	if (unit_failed())
-		return;
-
-	if (th == NULL && uh == NULL)
-		return;
-
-	check_transport_csum(ih, th, uh);
-}
-
-/*
- * Layers 3 & 4 Tests:  Given packets in forward_t.h, executes all actions 
- * with random data, checking for consistency described in check_packet().
- */
-
-void
-test_l3_l4(void)
-{
-	struct ofp_action action;
-	uint16_t a_type;
-	struct sk_buff *skb;
-	struct sw_flow_key key;
-	unsigned int i, j;
-	uint16_t eth_proto;
-	int ret = 0;
-
-	for (i = 0; i < num_packets; i++) {
-		skb = alloc_skb(packets[i].len, GFP_KERNEL);
-		if (!skb) {
-			unit_fail("Couldn't allocate %uth skb", i);
-			return;
-		}
-
-		memcpy(skb_put(skb, packets[i].len), packets[i].data,
-					packets[i].len);
-
-		skb_set_mac_header(skb, 0);
-		flow_extract(skb, 0, &key);
-		eth_proto = ntohs(key.dl_type);
-
-		check_packet(skb, NULL, packets+i);
-		if (unit_failed())
-			return;
-
-		for (a_type = OFPAT_SET_DL_SRC;
-			 a_type <= OFPAT_SET_TP_DST;
-			 a_type++)
-		{
-			action.type = htons(a_type);
-			set_action_data(skb, NULL, &action);
-			for(j = 0; j < 2; j++) {
-				skb = execute_setter(skb, eth_proto, &key, &action);
-				check_packet(skb, &action, packets+i);
-				if (unit_failed()) {
-					unit_fail("Packet %u inconsistent "
-						  "after setter on action "
-						  "type %d, iteration %u",
-						  i, action.type, j);
-					return;
-				}
-				set_action_data(skb, &key, &action);
-			}
-		}
-
-		kfree_skb(skb);
-
-		if (ret != 0)
-			break;
-	}
-
-	if (ret == 0)
-		printk("\nL3/L4 actions test passed.\n");
-}
-
-int
-test_vlan(void)
-{
-	struct ofp_action action;
-	struct sk_buff *skb;
-	struct sw_flow_key key;
-	unsigned int i;
-	uint16_t eth_proto;
-	int ret = 0;
-	struct vlan_ethhdr *vh;
-	struct ethhdr *eh;
-	struct net_device dev;
-	uint16_t new_id, orig_id;
-
-
-	memset((char *)&dev, '\0', sizeof(dev));
-
-	printk("Testing vlan\n");
-	for (i = 0; i < num_packets; i++) {
-		skb = alloc_skb(packets[i].len, GFP_KERNEL);
-		if (!skb) {
-			unit_fail("Couldn't allocate %uth skb", i);
-			return -ENOMEM;
-		}
-
-		memcpy(skb_put(skb, packets[i].len), packets[i].data,
-					packets[i].len);
-		skb->dev = &dev;
-
-		skb_set_mac_header(skb, 0);
-		flow_extract(skb, 0, &key);
-		eth_proto = ntohs(key.dl_type);
-
-#if 0
-		if ((ret = check_packet(skb, NULL, packets+i)) < 0) {
-			unit_fail("Packet %u has incorrect checksum unmodified",
-					i);
-			goto free_skb;
-		}
-#endif
-
-		eh = eth_hdr(skb);
-		orig_id = eh->h_proto;
-
-		action.type = htons(OFPAT_SET_VLAN_VID);
-
-		// Add a random vlan tag
-		new_id = (uint16_t) random32() & VLAN_VID_MASK;
-		action.arg.vlan_vid = new_id;
-		skb = execute_setter(skb, eth_proto, &key, &action);
-		vh = vlan_eth_hdr(skb);
-		if (ntohs(vh->h_vlan_TCI) != new_id) {
-			unit_fail("add: vlan id doesn't match: %#x != %#x", 
-					ntohs(vh->h_vlan_TCI), new_id);
-			return -1;
-		}
-		flow_extract(skb, 0, &key);
-#if 0
-		if ((ret = check_packet(skb, NULL, packets+i)) < 0) {
-			unit_fail("Packet %u has incorrect checksum after adding vlan",
-				  i);
-			goto free_skb;
-		}
-#endif
-
-		// Modify the tag
-		new_id = (uint16_t) random32() & VLAN_VID_MASK;
-		action.arg.vlan_vid = new_id;
-		skb = execute_setter(skb, eth_proto, &key, &action);
-		vh = vlan_eth_hdr(skb);
-		if (ntohs(vh->h_vlan_TCI) != new_id) {
-			unit_fail("mod: vlan id doesn't match: %#x != %#x", 
-					ntohs(vh->h_vlan_TCI), new_id);
-			return -1;
-		}
-		flow_extract(skb, 0, &key);
-#if 0
-		if ((ret = check_packet(skb, NULL, packets+i)) < 0) {
-			unit_fail("Packet %u has incorrect checksum after modifying vlan",
-				  i);
-			goto free_skb;
-		}
-#endif
-
-		// Remove the tag
-		action.type = htons(OFPAT_STRIP_VLAN);
-		skb = execute_setter(skb, eth_proto, &key, &action);
-
-		eh = eth_hdr(skb);
-
-		if (eh->h_proto != orig_id) {
-			unit_fail("del: vlan id doesn't match: %#x != %#x", 
-			  ntohs(eh->h_proto), ntohs(orig_id));
-			return -1;
-		}
-#if 0
-		if ((ret = check_packet(skb, NULL, packets+i)) < 0) {
-			unit_fail("Packet %u has incorrect checksum after removing vlan",
-				  i);
-			goto free_skb;
-		}
-
-	free_skb:
-#endif
-
-		kfree_skb(skb);
-
-		if (ret != 0)
-			break;
-	}
-
-	if (ret == 0)
-		printk("\nVLAN actions test passed.\n");
-
-	return ret;
-}
-
-/*
- * Actual test:  Given packets in forward_t.h, executes all actions with random
- * data, checking for consistency described in check_packet().
- */
-
-void
-run_forward_t(void)
-{
-	test_vlan();
-	test_l3_l4();
-}
diff --git a/datapath/table_t.c b/datapath/table_t.c
deleted file mode 100644
index a824804b4..000000000
--- a/datapath/table_t.c
+++ /dev/null
@@ -1,874 +0,0 @@
-/*
- * Distributed under the terms of the GNU GPL version 2.
- * Copyright (c) 2007, 2008 The Board of Trustees of The Leland 
- * Stanford Junior University
- */
-
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/vmalloc.h>
-#include <linux/random.h>
-#include <linux/rcupdate.h>
-
-#include "flow.h"
-#include "table.h"
-#include "openflow.h"
-#include "unit.h"
-
-static const char *
-table_name(struct sw_table *table)
-{
-	struct sw_table_stats stats;
-	table->stats(table, &stats);
-	return stats.name;
-}
-
-static unsigned long int
-table_max_flows(struct sw_table *table)
-{
-	struct sw_table_stats stats;
-	table->stats(table, &stats);
-	return stats.max_flows;
-}
-
-static struct sw_flow *flow_zalloc(int n_actions, gfp_t flags) 
-{
-	struct sw_flow *flow = flow_alloc(n_actions, flags);
-	if (flow) {
-		struct sw_flow_actions *sfa = flow->sf_acts;
-		memset(flow, 0, sizeof *flow);
-		flow->sf_acts = sfa;
-	}
-	return flow;
-}
-
-static void
-simple_insert_delete(struct sw_table *swt, uint32_t wildcards)
-{
-	struct sw_flow *a_flow = flow_zalloc(0, GFP_KERNEL);
-	struct sw_flow *b_flow = flow_zalloc(0, GFP_KERNEL);
-	struct sw_flow *found;
-
-	if (!swt) {
-		unit_fail("table creation failed");
-		return;
-	}
-
-	printk("simple_insert_delete: testing %s table\n", table_name(swt));
-	*((uint32_t*)a_flow->key.dl_src) = 0x12345678;
-	*((uint32_t*)b_flow->key.dl_src) = 0x87654321;
-
-	a_flow->key.nw_src	= 0xdeadbeef;
-	b_flow->key.nw_src	= 0x001dd0d0;
-
-	a_flow->key.wildcards = wildcards;
-	b_flow->key.wildcards = wildcards;
-
-	if (!(swt->insert(swt, a_flow)))
-		unit_fail("insert failed");
-	found = swt->lookup(swt, &a_flow->key);
-	if(found != a_flow)
-		unit_fail("%p != %p", found, a_flow);
-	if (swt->lookup(swt, &b_flow->key))
-		unit_fail("lookup should not succeed (1)");
-
-	swt->delete(swt, &a_flow->key, 0, 0);
-	if (swt->lookup(swt, &a_flow->key))
-		unit_fail("lookup should not succeed (3)");
-
-	flow_free(b_flow);
-	swt->destroy(swt);
-}
-
-static void
-multiple_insert_destroy(struct sw_table *swt, int inserts, uint32_t wildcards,
-			int min_collisions, int max_collisions)
-{
-	int i;
-	int col = 0;
-
-	if (!swt) {
-		unit_fail("table creation failed");
-		return;
-	}
-
-	printk("inserting %d flows into %s table with max %lu flows: ",
-				inserts, table_name(swt), table_max_flows(swt));
-	for(i = 0; i < inserts; ++i){
-		struct sw_flow *a_flow = flow_zalloc(0, GFP_KERNEL);
-		*((uint32_t*)&(a_flow->key.dl_src[0])) = random32();
-		a_flow->key.nw_src    = random32();
-		a_flow->key.wildcards = wildcards;
-
-		if(!swt->insert(swt, a_flow)) {
-			col++;
-			flow_free(a_flow);
-		}
-	}
-	printk("%d failures\n", col);
-	if (min_collisions <= col && col <= max_collisions)
-		printk("\tmin = %d <= %d <= %d = max, OK.\n",
-					min_collisions, col, max_collisions);
-	else {
-		if (col < min_collisions)
-			unit_fail("too few collisions (%d < %d)",
-				  col, min_collisions);
-		else if (col > max_collisions)
-			unit_fail("too many collisions (%d > %d)",
-				  col, max_collisions);
-		printk("(This is statistically possible "
-					"but should not occur often.)\n");
-	}
-	
-	swt->destroy(swt);
-}
-
-static void
-set_random_key(struct sw_flow_key *key, uint32_t wildcards)
-{
-	key->nw_src = random32();
-	key->nw_dst = random32();
-	key->in_port = (uint16_t) random32();
-	key->dl_vlan = (uint16_t) random32();
-	key->dl_type = (uint16_t) random32();
-	key->tp_src = (uint16_t) random32();
-	key->tp_dst = (uint16_t) random32();
-	key->wildcards = wildcards;
-	*((uint32_t*)key->dl_src) = random32();
-	*(((uint32_t*)key->dl_src) + 1) = random32();
-	*((uint32_t*)key->dl_dst) = random32();
-	*(((uint32_t*)key->dl_dst) + 1) = random32();
-	key->nw_proto = (uint8_t) random32();
-}
-
-struct flow_key_entry {
-	struct sw_flow_key key;
-	struct list_head node;
-};
-
-/*
- * Allocates memory for 'n_keys' flow_key_entrys.  Initializes the allocated
- * keys with random values, setting their wildcard values to 'wildcards', and
- * places them all in a list.  Returns a pointer to a flow_key_entry that
- * serves solely as the list's head (its key has not been set).  If allocation
- * fails, returns NULL.  Returned pointer should be freed with vfree (which
- * frees the memory associated with the keys as well.)
- */
-
-static struct flow_key_entry *
-allocate_random_keys(int n_keys, uint32_t wildcards)
-{
-	struct flow_key_entry *entries, *pos;
-	struct list_head *keys;
-
-	if (n_keys < 0)
-		return NULL;
-
-	entries = vmalloc((n_keys+1) * sizeof *entries);
-	if (entries == NULL) {
-		unit_fail("cannot allocate memory for %u keys",
-					n_keys);
-		return NULL;
-	}
-
-	keys = &entries->node;
-	INIT_LIST_HEAD(keys);
-
-	for(pos = entries+1; pos < (entries + n_keys + 1); pos++) {
-		set_random_key(&pos->key, wildcards);
-		list_add(&pos->node, keys);
-	}
-
-	return entries;
-}
-
-/*
- * Attempts to insert the first 'n_flows' flow keys in list 'keys' into table
- * 'swt', where 'keys' is a list of flow_key_entrys.  key_entrys that are
- * inserted into the table are removed from the 'keys' list and placed in
- * 'added' list.  Returns -1 if flow memory allocation fails, else returns the
- * number of flows that were actually inserted (some attempts might fail due to
- * collisions).
- */
-
-static int
-insert_flows(struct sw_table *swt, struct list_head *keys, struct list_head *added, int n_flows)
-{
-	struct flow_key_entry *pos, *next;
-	int cnt;
-
-	cnt = 0;
-
-
-	list_for_each_entry_safe (pos, next, keys, node) {
-		struct sw_flow *flow = flow_zalloc(0, GFP_KERNEL);
-		if (flow == NULL) {
-			unit_fail("Could only allocate %u flows", cnt);
-			return -1;
-		}
-
-		flow->key = pos->key;
-
-		if (!swt->insert(swt, flow)) {
-			flow_free(flow);
-			list_del(&pos->node);
-		} else {
-			list_del(&pos->node);
-			list_add(&pos->node, added);
-			cnt++;
-			if (n_flows != -1 && cnt == n_flows)
-				break;
-		}
-	}
-
-	return cnt;
-}
-
-/*
- * Finds and returns the flow_key_entry in list 'keys' matching the passed in
- * flow's key.  If not found, returns NULL.
- */
-
-static struct flow_key_entry *
-find_flow(struct list_head *keys, struct sw_flow *flow)
-{
-	struct flow_key_entry *pos;
-
-	list_for_each_entry(pos, keys, node) {
-		if(!memcmp(&pos->key, &flow->key, sizeof(struct sw_flow_key)))
-			return pos;
-	}
-
-	return NULL;
-}
-
-/*
- * Checks that all flow_key_entrys in list 'keys' return successful lookups on
- * the table 'swt'.
- */
-
-static int
-check_lookup(struct sw_table *swt, struct list_head *keys)
-{
-	struct flow_key_entry *pos;
-
-	list_for_each_entry(pos, keys, node) {
-		if(swt->lookup(swt, &pos->key) == NULL)
-			return -1;
-	}
-
-	return 0;
-}
-
-/*
- * Checks that all flow_key_entrys in list 'keys' DO NOT return successful
- * lookups in the 'swt' table.
- */
-
-static int
-check_no_lookup(struct sw_table *swt, struct list_head *keys)
-{
-	struct flow_key_entry *pos;
-
-	list_for_each_entry(pos, keys, node) {
-		if(swt->lookup(swt, &pos->key) != NULL)
-			return -1;
-	}
-
-	return 0;
-}
-
-
-struct check_iteration_state
-{
-	int n_found;
-	struct list_head *to_find;
-	struct list_head *found;
-};
-
-static int
-check_iteration_callback(struct sw_flow *flow, void *private) 
-{
-	struct check_iteration_state *s = private;
-	struct flow_key_entry *entry;
-
-	entry = find_flow(s->to_find, flow);
-	if (entry == NULL) {
-		unit_fail("UNKNOWN ITERATOR FLOW %p", flow);
-		rcu_read_unlock();
-		return 1;
-	}
-	s->n_found++;
-	list_del(&entry->node);
-	list_add(&entry->node, s->found);
-	return 0;
-}
-
-/*
- * Compares an iterator's view of the 'swt' table to the list of
- * flow_key_entrys in 'to_find'.  flow_key_entrys that are matched are removed
- * from the 'to_find' list and placed in the 'found' list.  Returns -1 if the
- * iterator cannot be initialized or it encounters a flow with a key not in
- * 'to_find'.  Else returns the number of flows found by the iterator
- * (i.e. there might still be flow keys in the 'to_find' list that were not
- * encountered by the iterator.  it is up to the caller to determine if that is
- * acceptable behavior)
- */
-
-static int
-check_iteration(struct sw_table *swt, struct list_head *to_find, struct list_head *found)
-{
-	struct sw_flow_key key;
-	struct sw_table_position position;
-	struct check_iteration_state state;
-
-	memset(&key, 0, sizeof key);
-	key.wildcards = -1;
-
-	memset(&position, 0, sizeof position);
-
-	state.n_found = 0;
-	state.to_find = to_find;
-	state.found = found;
-
-	rcu_read_lock();
-	swt->iterate(swt, &key, &position, check_iteration_callback, &state);
-	rcu_read_unlock();
-
-	return state.n_found;
-}
-
-/*
- * Deletes from table 'swt' keys from the list of flow_key_entrys 'keys'.
- * Removes flow_key_entrys of deleted flows from 'keys' and places them in the
- * 'deleted' list.  If 'del_all' == 1, all flows in 'keys' will be deleted,
- * else only every third key will be deleted.  Returns the number flows deleted
- * from the table.
- */
-
-static int
-delete_flows(struct sw_table *swt, struct list_head *keys,
-		 struct list_head *deleted, uint8_t del_all)
-{
-	struct flow_key_entry *pos, *next;
-	int i, n_del, total_del;
-
-	total_del = 0;
-	i = 0;
-
-	list_for_each_entry_safe (pos, next, keys, node) {
-		if (del_all == 1 || i % 3 == 0) {
-			n_del = swt->delete(swt, &pos->key, 0, 0);
-			if (n_del > 1) {
-				unit_fail("%d flows deleted for one entry", n_del);
-				unit_fail("\tfuture 'errors' could just be product duplicate flow_key_entries");
-				unit_fail("THIS IS VERY UNLIKELY...SHOULDN'T HAPPEN OFTEN");
-			}
-			total_del += n_del;
-			list_del(&pos->node);
-			list_add(&pos->node, deleted);
-		}
-		i++;
-	}
-
-	return total_del;
-}
-
-/*
- * Checks that both iteration and lookups are consistent with the caller's view
- * of the table.  In particular, checks that all keys in flow_key_entry list
- * 'deleted' do not show up in lookup or iteration, and keys in flow_key_entry
- * list 'added' do show up.  'tmp' should be an empty list that can be used for
- * iteration.  References to list_head pointers are needed for 'added' and 'tmp'
- * because iteration will cause the list_heads to change.  Function thus
- * switches 'added' to point to the list of added keys after the iteration.
- */
-
-static int
-check_lookup_and_iter(struct sw_table *swt, struct list_head *deleted,
-			  struct list_head **added, struct list_head **tmp)
-{
-	struct list_head *tmp2;
-	int ret;
-
-	if (check_no_lookup(swt, deleted) < 0) {
-		unit_fail("Uninserted flows returning lookup");
-		return -1;
-	}
-
-	if (check_lookup(swt, *added) < 0) {
-		unit_fail("Inserted flows not returning lookup");
-		return -1;
-	}
-
-	ret = check_iteration(swt, *added, *tmp);
-
-	tmp2 = *added;
-	*added = *tmp;
-	*tmp = tmp2;
-
-	if ((*tmp)->next != *tmp) {
-		unit_fail("WARNING: not all flows in 'added' found by iterator");
-		unit_fail("\tcould be a product of duplicate flow_key_entrys, though should be VERY rare.");
-		/* To avoid reoccurence */
-		(*tmp)->next = (*tmp)->prev = *tmp;
-	}
-
-	return ret;
-}
-
-/*
- * Verifies iteration and lookup after inserting 'n_flows', then after deleting
- * some flows, and once again after deleting all flows in table 'swt'.
- */
-
-static int
-iterator_test(struct sw_table *swt, int n_flows, uint32_t wildcards)
-{
-	struct flow_key_entry *allocated, h1, h2;
-	struct list_head *added, *deleted, *tmp;
-	int ret, n_del, success;
-
-	INIT_LIST_HEAD(&h1.node);
-	INIT_LIST_HEAD(&h2.node);
-
-	success = -1;
-
-	allocated = allocate_random_keys(n_flows, wildcards);
-	if(allocated == NULL)
-		return success;
-
-	deleted = &allocated->node;
-	added = &h1.node;
-	tmp = &h2.node;
-
-	ret = insert_flows(swt, deleted, added, -1);
-	if (ret < 0)
-		goto iterator_test_destr;
-
-	n_flows = ret;
-
-	ret = check_lookup_and_iter(swt, deleted, &added, &tmp);
-	if (ret < 0) {
-		unit_fail("Bad lookup after insertion");
-		goto iterator_test_destr;
-	} else if (ret != n_flows) {
-		unit_fail("Iterator only found %d of %d flows",
-			  ret, n_flows);
-		goto iterator_test_destr;
-	}
-
-	n_del = delete_flows(swt, added, deleted, 0);
-
-	ret = check_lookup_and_iter(swt, deleted, &added, &tmp);
-	if (ret < 0) {
-		unit_fail("Bad lookup after some deletion");
-		goto iterator_test_destr;
-	} else if (ret + n_del != n_flows) {
-		unit_fail("iterator after deletion inconsistent");
-		unit_fail("\tn_del = %d, n_found = %d, n_flows = %d",
-			  n_del, ret, n_flows);
-		goto iterator_test_destr;
-	}
-
-	n_flows -= n_del;
-
-	n_del = delete_flows(swt, added, deleted, 1);
-	if (n_del != n_flows) {
-		unit_fail("Not all flows deleted - only %d of %d",
-			  n_del, n_flows);
-		goto iterator_test_destr;
-	}
-
-	ret = check_lookup_and_iter(swt, deleted, &added, &tmp);
-	if (ret < 0) {
-		unit_fail("Bad lookup after all deletion");
-		goto iterator_test_destr;
-	} else if (ret != 0) {
-		unit_fail("Empty table iterator failed.  %d flows found",
-			  ret);
-		goto iterator_test_destr;
-	}
-
-	success = 0;
-
-iterator_test_destr:
-	allocated->key.wildcards = OFPFW_ALL;
-	swt->delete(swt, &allocated->key, 0, 0);
-	vfree(allocated);
-	return success;
-}
-
-
-/*
- * Checks lookup and iteration consistency after adding one flow, adding the
- * flow again, and then deleting the flow from table 'swt'.
- */
-
-static int
-add_test(struct sw_table *swt, uint32_t wildcards)
-{
-	struct flow_key_entry *allocated, h1, h2;
-	struct list_head *added, *deleted, *tmp, *tmp2;
-	int ret, success = -1;
-
-	INIT_LIST_HEAD(&h1.node);
-	INIT_LIST_HEAD(&h2.node);
-
-	allocated = allocate_random_keys(2, wildcards);
-	if (allocated == NULL)
-		return success;
-
-	deleted = &allocated->node;
-	added = &h1.node;
-	tmp = &h2.node;
-
-	ret = check_lookup_and_iter(swt, deleted, &added, &tmp);
-	if (ret < 0) {
-		unit_fail("Bad lookup before table modification");
-		goto add_test_destr;
-	} else if (ret != 0) {
-		unit_fail("Iterator on empty table found %d flows",
-			  ret);
-		goto add_test_destr;
-	}
-
-	if (insert_flows(swt, deleted, added, 1) != 1) {
-		unit_fail("Cannot add one flow to table");
-		goto add_test_destr;
-	}
-
-	ret = check_lookup_and_iter(swt, deleted, &added, &tmp);
-	if (ret < 0) {
-		unit_fail("Bad lookup after single add");
-		goto add_test_destr;
-	} else if (ret != 1) {
-		unit_fail("Iterator on single add found %d flows",
-			  ret);
-		goto add_test_destr;
-	}
-
-	/* Re-adding flow */
-	if (insert_flows(swt, added, tmp, 1) != 1) {
-		unit_fail("Cannot insert same flow twice");
-		goto add_test_destr;
-	}
-
-	tmp2 = added;
-	added = tmp;
-	tmp = tmp2;
-
-	ret = check_lookup_and_iter(swt, deleted, &added, &tmp);
-	if (ret < 0) {
-		unit_fail("Bad lookup after double add");
-		goto add_test_destr;
-	} else if (ret != 1) {
-		unit_fail("Iterator on double add found %d flows",
-			  ret);
-		goto add_test_destr;
-	}
-
-	ret = delete_flows(swt, added, deleted, 1);
-	if (ret != 1) {
-		unit_fail("Unexpected %d flows deleted", ret);
-		goto add_test_destr;
-	}
-
-	ret = check_lookup_and_iter(swt, deleted, &added, &tmp);
-	if (ret < 0) {
-		unit_fail("Bad lookup after delete.");
-		goto add_test_destr;
-	} else if (ret != 0) {
-		unit_fail("unexpected %d flows found delete", ret);
-		goto add_test_destr;
-	}
-
-	success = 0;
-
-add_test_destr:
-	allocated->key.wildcards = OFPFW_ALL;
-	swt->delete(swt, &allocated->key, 0, 0);
-	vfree(allocated);
-	return success;
-}
-
-/*
- * Checks lookup and iteration consistency after each deleting a non-existent
- * flow, adding and then deleting a flow, adding the flow again, and then
- * deleting the flow twice in table 'swt'.
- */
-
-static int
-delete_test(struct sw_table *swt, uint32_t wildcards)
-{
-	struct flow_key_entry *allocated, h1, h2;
-	struct list_head *added, *deleted, *tmp, *tmp2;
-	int i, ret, success = -1;
-
-	INIT_LIST_HEAD(&h1.node);
-	INIT_LIST_HEAD(&h2.node);
-
-	allocated = allocate_random_keys(2, wildcards);
-	if (allocated == NULL)
-		return success;
-
-	/* Not really added...*/
-
-	added = &allocated->node;
-	deleted = &h1.node;
-	tmp = &h2.node;
-
-	ret = delete_flows(swt, added, deleted, 1);
-	if (ret != 0) {
-		unit_fail("Deleting non-existent keys from table returned unexpected value %d",
-			  ret);
-			goto delete_test_destr;
-	}
-
-	for (i = 0; i < 3; i++) {
-		ret = check_lookup_and_iter(swt, deleted, &added, &tmp);
-		if (ret < 0) {
-			if (i == 0)
-				unit_fail("Loop %d. Bad lookup before modification.", i);
-			else
-				unit_fail("Loop %d. Bad lookup after delete.", i);
-			goto delete_test_destr;
-		} else if (ret != 0) {
-			if(i == 0)
-				unit_fail("Loop %d. Unexpected %d flows found before modification",
-					  i, ret);
-			else
-				unit_fail("Loop %d. Unexpected %d flows found after delete",
-					  i, ret);
-			goto delete_test_destr;
-		}
-
-		if(i == 2)
-			break;
-
-		if (insert_flows(swt, deleted, added, 1) != 1) {
-			unit_fail("loop %d: cannot add flow to table", i);
-			goto delete_test_destr;
-		}
-
-		ret = check_lookup_and_iter(swt, deleted, &added, &tmp);
-		if (ret < 0) {
-			unit_fail("loop %d: bad lookup after single add.", i);
-			goto delete_test_destr;
-		} else if (ret != 1) {
-			unit_fail("loop %d: unexpected %d flows found after single add",
-				  i, ret);
-			goto delete_test_destr;
-		}
-
-		ret = delete_flows(swt, added, deleted, 1);
-		if (ret != 1) {
-			unit_fail("loop %d: deleting inserted key from table returned unexpected value %d",
-						i, ret);
-			goto delete_test_destr;
-		}
-	}
-
-
-	ret = delete_flows(swt, deleted, tmp, 1);
-
-	tmp2 = deleted;
-	deleted = tmp2;
-	tmp = tmp2;
-
-	ret = check_lookup_and_iter(swt, deleted, &added, &tmp);
-	if (ret < 0) {
-		unit_fail("Bad lookup after double delete.");
-		goto delete_test_destr;
-	} else if (ret != 0) {
-		unit_fail("Unexpected %d flows found after double delete", ret);
-		goto delete_test_destr;
-	}
-
-	success = 0;
-
-delete_test_destr:
-	allocated->key.wildcards = OFPFW_ALL;
-	swt->delete(swt, &allocated->key, 0, 0);
-	vfree(allocated);
-	return success;
-}
-
-/*
- * Randomly adds and deletes from a set of size 'n_flows', looping for 'i'
- * iterations.
- */
-
-static int
-complex_add_delete_test(struct sw_table *swt, int n_flows, int i, uint32_t wildcards)
-{
-	struct flow_key_entry *allocated, h1, h2;
-	struct list_head *added, *deleted, *tmp;
-	int cnt, ret, n_added, n_deleted, success = -1;
-	uint8_t del_all;
-
-	INIT_LIST_HEAD(&h1.node);
-	INIT_LIST_HEAD(&h2.node);
-
-	allocated = allocate_random_keys(n_flows, wildcards);
-	if (allocated == NULL)
-		return success;
-
-	deleted = &allocated->node;
-	added = &h1.node;
-	tmp = &h2.node;
-
-	n_deleted = n_flows;
-	n_added = 0;
-
-	for (;i > 0; i--) {
-		if (n_deleted != 0 && random32() % 2 == 0) {
-			cnt = random32() % n_deleted;
-			cnt = insert_flows(swt, deleted, added, cnt);
-			if (cnt < 0)
-				goto complex_test_destr;
-			n_deleted -= cnt;
-			n_added += cnt;
-		} else {
-			if (random32() % 7 == 0)
-				del_all = 1;
-			else
-				del_all = 0;
-			cnt = delete_flows(swt, added, deleted, del_all);
-			n_deleted += cnt;
-			n_added -= cnt;
-		}
-
-		ret = check_lookup_and_iter(swt, deleted, &added, &tmp);
-		if (ret < 0) {
-			unit_fail("Bad lookup on iteration %d.", i);
-			goto complex_test_destr;
-		}
-	}
-
-	delete_flows(swt, added, deleted, 1);
-	ret = check_lookup_and_iter(swt, deleted, &added, &tmp);
-	if (ret < 0) {
-		unit_fail("Bad lookup on end deletion.");
-		goto complex_test_destr;
-	} else if (ret != 0) {
-		unit_fail("Unexpected %d flows found on end deletion", ret);
-		goto complex_test_destr;
-	}
-
-	success = 0;
-
-complex_test_destr:
-	allocated->key.wildcards = OFPFW_ALL;
-	swt->delete(swt, &allocated->key, 0, 0);
-	vfree(allocated);
-	return success;
-
-}
-
-void run_table_t(void)
-{
-	int linear_max, hash_buckets, hash2_buckets1;
-	int hash2_buckets2, num_flows, num_iterations;
-	int i;
-
-	struct sw_table *swt;
-
-	/* Most basic operations. */
-	simple_insert_delete(table_linear_create(2048), 0);
-	simple_insert_delete(table_hash_create(0x04C11DB7, 2048), 0);
-	simple_insert_delete(table_hash2_create(0x04C11DB7, 2048,
-						0x1EDC6F41, 2048), 0);
-
-	/* Linear table operations. */
-	multiple_insert_destroy(table_linear_create(2048), 1024, 0, 0, 0);
-	multiple_insert_destroy(table_linear_create(2048), 2048, 0, 0, 0);
-	multiple_insert_destroy(table_linear_create(2048), 8192, 0,
-				8192 - 2048, 8192 - 2048);
-
-	/* Hash table operations. */
-	multiple_insert_destroy(table_hash_create(0x04C11DB7, 2048), 1024, 0,
-				100, 300);
-	multiple_insert_destroy(table_hash_create(0x04C11DB7, 2048), 2048, 0,
-				500, 1000);
-	multiple_insert_destroy(table_hash_create(0x04C11DB7, 1 << 20), 8192, 0,
-				0, 50);
-	multiple_insert_destroy(table_hash_create(0x04C11DB7, 1 << 20), 65536, 0,
-				1500, 3000);
-
-	/* Hash table 2, two hash functions. */
-	multiple_insert_destroy(table_hash2_create(0x04C11DB7, 2048,
-						0x1EDC6F41, 2048), 1024, 0, 0, 20);
-	multiple_insert_destroy(table_hash2_create(0x04C11DB7, 2048,
-						0x1EDC6F41, 2048), 2048, 0, 50, 200);
-	multiple_insert_destroy(table_hash2_create(0x04C11DB7, 1<<20,
-						0x1EDC6F41, 1<<20), 8192, 0, 0, 20);
-	multiple_insert_destroy(table_hash2_create(0x04C11DB7, 1<<20,
-						0x1EDC6F41, 1<<20), 65536, 0, 0, 20);
-
-	/* Hash table 2, one hash function. */
-	multiple_insert_destroy(table_hash2_create(0x04C11DB7, 2048,
-						0x04C11DB7, 2048), 1024, 0, 0, 50);
-	multiple_insert_destroy(table_hash2_create(0x04C11DB7, 2048,
-						0x04C11DB7, 2048), 2048, 0, 100, 300);
-	multiple_insert_destroy(table_hash2_create(0x04C11DB7, 1<<20,
-						0x04C11DB7, 1<<20), 8192, 0, 0, 20);
-	multiple_insert_destroy(table_hash2_create(0x04C11DB7, 1<<20,
-						0x04C11DB7, 1<<20), 65536, 0, 0, 100);
-	multiple_insert_destroy(table_hash2_create(0x04C11DB7, 1<<20,
-						0x04C11DB7, 1<<20), 1<<16, 0, 0, 100);
-
-	linear_max = 2048;
-	hash_buckets = 2048;
-	hash2_buckets1 = 1024;
-	hash2_buckets2 = 1024;
-
-	num_flows = 2300;
-	num_iterations = 100;
-
-	printk("\nTesting on each table type:\n");
-	printk("  iteration_test on 0 flows\n");
-	printk("  iteration_test on %d flows\n", num_flows);
-	printk("  add_test\n");
-	printk("  delete_test\n");
-	printk("  complex_add_delete_test with %d flows and %d iterations\n\n",
-				num_flows, num_iterations);
-
-	for (i = 0; i < 3; i++) {
-		unsigned int mask = i == 0 ?  : 0;
-
-		if (unit_failed())
-			return;
-
-		mask = 0;
-		switch (i) {
-		case 0:
-			swt = table_linear_create(linear_max);
-			break;
-		case 1:
-			swt = table_hash_create (0x04C11DB7, hash_buckets);
-			break;
-		case 2:
-			swt = table_hash2_create(0x04C11DB7, hash2_buckets1,
-						 0x1EDC6F41, hash2_buckets2);
-			break;
-		default:
-			BUG();
-			return;
-		}
-
-		if (swt == NULL) {
-			unit_fail("failed to allocate table %d", i);
-			return;
-		}
-		printk("Testing %s table with %d buckets and %d max flows...\n",
-					table_name(swt), hash_buckets, num_flows);
-		iterator_test(swt, 0, mask);
-		iterator_test(swt, num_flows, mask);
-		add_test(swt, mask);
-		delete_test(swt, mask);
-		complex_add_delete_test(swt, num_flows, num_iterations, mask);
-		swt->destroy(swt);
-	}
-}
-
diff --git a/datapath/tests/.gitignore b/datapath/tests/.gitignore
deleted file mode 100644
index 35e75b7ab..000000000
--- a/datapath/tests/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-/Makefile
-/Makefile.in
-/forward_t.h
-/fwdhgen
diff --git a/datapath/tests/Makefile.am b/datapath/tests/Makefile.am
deleted file mode 100644
index fc5fd8f82..000000000
--- a/datapath/tests/Makefile.am
+++ /dev/null
@@ -1,7 +0,0 @@
-noinst_HEADERS = forward_t.h
-
-forward_t.h: gen_forward_t.pl example.pcap
-	perl $(srcdir)/gen_forward_t.pl $(srcdir)/example.pcap > forward_t.h.tmp
-	mv forward_t.h.tmp forward_t.h
-
-EXTRA_DIST = gen_forward_t.pl example.pcap
diff --git a/datapath/tests/example.pcap b/datapath/tests/example.pcap
deleted file mode 100644
index 7bda45893..000000000
Binary files a/datapath/tests/example.pcap and /dev/null differ
diff --git a/datapath/tests/gen_forward_t.pl b/datapath/tests/gen_forward_t.pl
deleted file mode 100755
index 6e12e2c93..000000000
--- a/datapath/tests/gen_forward_t.pl
+++ /dev/null
@@ -1,80 +0,0 @@
-#! /usr/bin/perl
-
-use warnings;
-use strict;
-
-if (@ARGV != 1) {
-    print "usage: $0 input.pcap > output.h\n";
-    print "where input.pcap is a packet capture in pcap format\n";
-    print "and output.c is a C header file containing the packets\n";
-    exit(1);
-}
-my ($in_file_name) = $ARGV[0];
-open(INPUT, '<', $in_file_name) or die "$in_file_name: open: $!\n";
-
-my ($file_header);
-if (read(INPUT, $file_header, 24) != 24) {
-    die "$in_file_name: could not read pcap header\n";
-}
-
-my ($s, $l);
-if (substr($file_header, 0, 4) eq pack('V', 0xa1b2c3d4)) {
-    ($s, $l) = ('v', 'V');
-} elsif (substr($file_header, 0, 4) eq pack('N', 0xa1b2c3d4)) {
-    ($s, $l) = ('n', 'N');
-} else {
-    die "$in_file_name: not a pcap file\n";
-}
-
-print <<'EOF';
-#ifndef DP_TEST_PACKETS_H
-#define DP_TEST_PACKETS_H 1
-
-struct pkt {
-	unsigned char *data;
-	unsigned int len;
-};
-EOF
-
-my ($n_packets) = 0;
-for (;;) {
-    my ($pkt_hdr) = must_read(16);
-    last if $pkt_hdr eq '';
-
-    my ($ts_sec, $ts_usec, $incl_len, $orig_len) = unpack("${l}4", $pkt_hdr);
-    print STDERR "warning: captured less than len %u\n"
-      if $incl_len < $orig_len;
-
-    my ($pkt) = must_read($incl_len);
-    die "$in_file_name: unexpected end of file\n" if !$pkt;
-
-    print "\nstatic unsigned char p${n_packets}[] = {";
-    my ($line_bytes) = 0;
-    for my $c (map(ord($_), split(//, $pkt))) {
-        if ($line_bytes++ % 13 == 0) {
-            print "\n";
-        }
-        printf " 0x%02x,", $c;
-    }
-    print "\n};\n";
-    $n_packets++;
-}
-
-print "\nstatic int num_packets = $n_packets;\n";
-print "\nstatic struct pkt packets[] = {\n";
-for my $i (0..$n_packets - 1) {
-    print "  { p$i, sizeof p$i },\n";
-}
-print "};\n";
-
-print "\n#endif\n";
-
-sub must_read {
-    my ($rq_bytes) = @_;
-    my ($data);
-    my ($nbytes) = read(INPUT, $data, $rq_bytes);
-    die "$in_file_name: read: $!\n" if !defined $nbytes;
-    die "$in_file_name: unexpected end of file\n"
-      if $nbytes && $nbytes != $rq_bytes;
-    return $data;
-}
diff --git a/datapath/tests/ofp_pcap.c b/datapath/tests/ofp_pcap.c
deleted file mode 100644
index e1b0d2261..000000000
--- a/datapath/tests/ofp_pcap.c
+++ /dev/null
@@ -1,97 +0,0 @@
-/* A cheap knock-off of the pcap library to remove that dependency. */
-
-#include <stdlib.h>
-#include <string.h>
-#include <arpa/inet.h>
-#include "ofp_pcap.h"
-
-int
-ofp_pcap_open(struct ofp_pcap *p, const char *fname, char *errbuf)
-{
-	FILE *fp;
-	struct pcap_file_header hdr;
-	size_t amt_read;
-
-	fp = fopen(fname, "r");
-
-	memset((char *)p, 0, sizeof(*p));
-
-	amt_read = fread((char *)&hdr, 1, sizeof(hdr), fp);
-	if (amt_read != sizeof(hdr)) {
-		snprintf(errbuf, OFP_PCAP_ERRBUF_SIZE, "error reading dump file");
-		goto error;
-	}
-
-	if (hdr.magic != TCPDUMP_MAGIC) {
-		hdr.magic         = SWAPLONG(hdr.magic);
-		hdr.version_major = SWAPSHORT(hdr.version_major);
-		hdr.version_minor = SWAPSHORT(hdr.version_minor);
-		hdr.thiszone      = SWAPLONG(hdr.thiszone);
-		hdr.sigfigs       = SWAPLONG(hdr.sigfigs);
-		hdr.snaplen       = SWAPLONG(hdr.snaplen);
-		hdr.linktype      = SWAPLONG(hdr.linktype);
-
-		p->swapped = 1;
-	}
-
-	p->fp = fp;
-	p->errbuf = errbuf;
-	p->bufsize = hdr.snaplen+sizeof(struct pcap_pkthdr);
-	p->buf = malloc(p->bufsize);
-	if (!p->buf) {
-		snprintf(errbuf, OFP_PCAP_ERRBUF_SIZE, "error allocating buffer");
-		goto error;
-	}
-
-	if (hdr.version_major < OFP_PCAP_VERSION_MAJOR) {
-		snprintf(errbuf, OFP_PCAP_ERRBUF_SIZE, "archaic file format");
-		goto error;
-	}
-
-	return 0;
-
-error:
-	if (p->buf)
-		free(p->buf);
-	return 1;
-}
-
-char *
-ofp_pcap_next(struct ofp_pcap *p, struct pcap_pkthdr *hdr)
-{
-	size_t amt_read;
-
-	amt_read = fread(hdr, 1, sizeof(*hdr), p->fp);
-	if (amt_read != sizeof(*hdr)) {
-		snprintf(p->errbuf, OFP_PCAP_ERRBUF_SIZE, "error reading dump file");
-		return NULL;
-	}
-
-	if (p->swapped) {
-		hdr->caplen = SWAPLONG(hdr->caplen);
-		hdr->len = SWAPLONG(hdr->len);
-		hdr->ts.tv_sec = SWAPLONG(hdr->ts.tv_sec);
-		hdr->ts.tv_usec = SWAPLONG(hdr->ts.tv_usec);
-	}
-
-	if (hdr->caplen > p->bufsize) {
-		snprintf(p->errbuf, OFP_PCAP_ERRBUF_SIZE, "error reading dump file");
-		return NULL;
-	}
-
-	amt_read = fread((char *)p->buf, 1, hdr->caplen, p->fp);
-	if (amt_read != hdr->caplen){
-		snprintf(p->errbuf, OFP_PCAP_ERRBUF_SIZE, "error reading dump file");
-		return NULL;
-	}
-
-	return p->buf;
-}
-
-void
-ofp_pcap_close(struct ofp_pcap *p)
-{
-	fclose(p->fp);
-	free(p->buf);
-}
-
diff --git a/datapath/tests/ofp_pcap.h b/datapath/tests/ofp_pcap.h
deleted file mode 100644
index 6bd2dcb3c..000000000
--- a/datapath/tests/ofp_pcap.h
+++ /dev/null
@@ -1,64 +0,0 @@
-#ifndef OFP_PCAP_H
-#define OFP_PCAP_H
-
-#include <stdint.h>
-#include <sys/time.h>
-#include <stdio.h>
-
-#define OFP_PCAP_VERSION_MAJOR 2
-#define OFP_PCAP_VERSION_MINOR 4
-
-#define TCPDUMP_MAGIC 0xa1b2c3d4
-
-#define OFP_LINKTYPE_ETHERNET 1
-
-#define OFP_PCAP_ERRBUF_SIZE  256
-
-/* Swap the byte order regardless of the architecture */
-#define SWAPLONG(x) \
-	((((x)&0xff)<<24) | (((x)&0xff00)<<8) | (((x)&0xff0000)>>8) | (((x)&0xff000000)>>24))
-#define SWAPSHORT(x) \
-	((((x)&0xff)<<8) | (((x)&0xff00)>>8))
-
-struct ofp_pcap {
-	FILE *fp;               /* File pointer to currently processed file */
-	int swapped;            /* Indicate whether endian-ness needs to change */
-	char *buf;              /* Buffer to hold packet data */
-	size_t bufsize;         /* Size of buffer */
-	char *errbuf;		    /* Pointer to buffer to hold error message */
-};
-
-struct pcap_file_header {
-	uint32_t magic;         /* Magic number */
-	uint16_t version_major; /* Version number major */
-	uint16_t version_minor; /* Version number minor */
-	int32_t  thiszone;      /* Gmt to local correction */
-	uint32_t sigfigs;       /* Accuracy of timestamps */
-	uint32_t snaplen;       /* Max length saved portion of each pkt */
-	uint32_t linktype;      /* Data link type (LINKTYPE_*) */
-};
-
-/*
- * This is a timeval as stored in disk in a dumpfile.
- * It has to use the same types everywhere, independent of the actual
- * `struct timeval'
- */
-struct pcap_timeval {
-	int32_t tv_sec;         /* Seconds */
-	int32_t tv_usec;        /* Microseconds */
-};
-
-/*
- * How a `pcap_pkthdr' is actually stored in the dumpfile.
- */
-struct pcap_pkthdr {
-	struct pcap_timeval ts; /* Time stamp */
-	uint32_t caplen;        /* Length of portion present */
-	uint32_t len;           /* Length this packet (off wire) */
-};
-
-int ofp_pcap_open(struct ofp_pcap *p, const char *fname, char *errbuf);
-char *ofp_pcap_next(struct ofp_pcap *p, struct pcap_pkthdr *hdr);
-void ofp_pcap_close(struct ofp_pcap *p);
-
-#endif /* ofp_pcap.h */
diff --git a/datapath/unit-exports.c b/datapath/unit-exports.c
deleted file mode 100644
index 324e77a7a..000000000
--- a/datapath/unit-exports.c
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Distributed under the terms of the GNU GPL version 2.
- * Copyright (c) 2007, 2008 The Board of Trustees of The Leland 
- * Stanford Junior University
- */
-
-#include "table.h"
-#include "flow.h"
-#include "crc32.h"
-#include "forward.h"
-#include <linux/module.h>
-
-EXPORT_SYMBOL(flow_alloc);
-EXPORT_SYMBOL(flow_cache);
-
-EXPORT_SYMBOL(table_hash_create);
-EXPORT_SYMBOL(table_hash2_create);
-EXPORT_SYMBOL(table_linear_create);
-
-EXPORT_SYMBOL(crc32_init);
-EXPORT_SYMBOL(crc32_calculate);
-
-EXPORT_SYMBOL(flow_extract);
-EXPORT_SYMBOL(execute_setter);
diff --git a/datapath/unit.c b/datapath/unit.c
deleted file mode 100644
index 73aee7bd2..000000000
--- a/datapath/unit.c
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Distributed under the terms of the GNU GPL version 2.
- * Copyright (c) 2007, 2008 The Board of Trustees of The Leland 
- * Stanford Junior University
- */
-
-#include <linux/autoconf.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/string.h>
-#include <linux/version.h>
-#include <linux/errno.h>
-
-#include "unit.h"
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
-static char run[1024];
-module_param_string(run, run, sizeof run, 0);
-MODULE_PARM_DESC(run_tests, "run=\"test1,[test2,...]\"\n");
-#else
-static char *run;
-MODULE_PARM(run, "s");
-#endif
-
-static int test_failed;
-static const char *test_name;
-
-void unit_fail_function(const char *function, const char *msg, ...) 
-{
-	va_list args;
-
-	printk("%s: FAIL: %s: ", test_name, function);
-	va_start(args, msg);
-	vprintk(msg, args);
-	va_end(args);
-	printk("\n");
-	test_failed = 1;
-}
-
-int unit_failed(void) 
-{
-	return test_failed;
-}
-
-static int run_test(const char *name, size_t len)
-{
-	static const struct test {
-		const char *name;
-		void (*func)(void);
-	} tests[] = {
-#define UNIT_TEST(NAME) {#NAME, run_##NAME},
-		UNIT_TESTS
-#undef UNIT_TEST
-	};
-
-	const struct test *p;
-
-	for (p = tests; p < &tests[ARRAY_SIZE(tests)]; p++)
-		if (len == strlen(p->name)
-			&& !memcmp(name, p->name, len)) {
-			test_name = p->name;
-			test_failed = 0;
-			p->func();
-			printk("%s: %s\n", test_name,
-						test_failed ? "FAIL" : "PASS");
-			return !test_failed;
-		}
-	printk("unknown unit test %.*s\n", (int) len, name);
-	return 0;
-}
-
-int unit_init(void)
-{
-	int n_pass = 0, n_fail = 0;
-	char *p = run;
-
-	if (p == NULL) {
-		p = "";
-	}
-	for (;;) {
-		static const char white_space[] = " \t\r\n\v,";
-		int len;
-
-		p += strspn(p, white_space);
-		if (!*p)
-			break;
-
-		len = strcspn(p, white_space);
-		if (run_test(p, len))
-			n_pass++;
-		else
-			n_fail++;
-		p += len;
-	}
-
-	if (n_pass + n_fail == 0)
-		printk("no tests specified (use run=\"test1 [test2...]\")\n");
-	else
-		printk("%d tests passed, %d failed\n", n_pass, n_fail);
-
-	return -ENODEV;
-}
-
-module_init(unit_init);
-MODULE_LICENSE("GPL");
diff --git a/datapath/unit.h b/datapath/unit.h
deleted file mode 100644
index 6d180a8b2..000000000
--- a/datapath/unit.h
+++ /dev/null
@@ -1,21 +0,0 @@
-#ifndef UNIT_H
-#define UNIT_H 1
-
-/* List of unit tests. */
-#define UNIT_TESTS				\
-	UNIT_TEST(table_t)					  \
-	UNIT_TEST(crc_t)						\
-	UNIT_TEST(forward_t)
-
-/* Prototype a function run_<NAME> for each of the unit tests. */
-#define UNIT_TEST(NAME) void run_##NAME(void);
-UNIT_TESTS
-#undef UNIT_TEST
-
-void unit_fail_function(const char *function, const char *msg, ...)
-	__attribute__((format(printf, 2, 3)));
-#define unit_fail(...) unit_fail_function(__func__, __VA_ARGS__)
-
-int unit_failed(void);
-
-#endif /* unit.h */