Home | History | Annotate | Line # | Download | only in libnpftest
npf_gc_test.c revision 1.1.2.2
      1  1.1.2.2  martin /*
      2  1.1.2.2  martin  * NPF connection tests.
      3  1.1.2.2  martin  *
      4  1.1.2.2  martin  * Public Domain.
      5  1.1.2.2  martin  */
      6  1.1.2.2  martin 
      7  1.1.2.2  martin #ifdef _KERNEL
      8  1.1.2.2  martin #include <sys/types.h>
      9  1.1.2.2  martin #include <sys/kmem.h>
     10  1.1.2.2  martin #endif
     11  1.1.2.2  martin 
     12  1.1.2.2  martin #include "npf.h"
     13  1.1.2.2  martin #include "npf_impl.h"
     14  1.1.2.2  martin #include "npf_conn.h"
     15  1.1.2.2  martin #include "npf_test.h"
     16  1.1.2.2  martin 
     17  1.1.2.2  martin static bool	lverbose = false;
     18  1.1.2.2  martin 
     19  1.1.2.2  martin static unsigned
     20  1.1.2.2  martin count_conns(npf_conndb_t *cd)
     21  1.1.2.2  martin {
     22  1.1.2.2  martin 	npf_conn_t *head = npf_conndb_getlist(cd), *conn = head;
     23  1.1.2.2  martin 	unsigned n = 0;
     24  1.1.2.2  martin 
     25  1.1.2.2  martin 	while (conn) {
     26  1.1.2.2  martin 		n++;
     27  1.1.2.2  martin 		conn = npf_conndb_getnext(cd, conn);
     28  1.1.2.2  martin 		if (conn == head) {
     29  1.1.2.2  martin 			break;
     30  1.1.2.2  martin 		}
     31  1.1.2.2  martin 	}
     32  1.1.2.2  martin 	return n;
     33  1.1.2.2  martin }
     34  1.1.2.2  martin 
     35  1.1.2.2  martin static struct mbuf *
     36  1.1.2.2  martin get_packet(unsigned i)
     37  1.1.2.2  martin {
     38  1.1.2.2  martin 	struct mbuf *m;
     39  1.1.2.2  martin 	struct ip *ip;
     40  1.1.2.2  martin 
     41  1.1.2.2  martin 	m = mbuf_get_pkt(AF_INET, IPPROTO_UDP,
     42  1.1.2.2  martin 	    "10.0.0.1", "172.16.0.1", 9000, 9000);
     43  1.1.2.2  martin 	(void)mbuf_return_hdrs(m, false, &ip);
     44  1.1.2.2  martin 	ip->ip_src.s_addr += i;
     45  1.1.2.2  martin 	return m;
     46  1.1.2.2  martin }
     47  1.1.2.2  martin 
     48  1.1.2.2  martin static bool
     49  1.1.2.2  martin enqueue_connection(unsigned i, bool expire)
     50  1.1.2.2  martin {
     51  1.1.2.2  martin 	struct mbuf *m = get_packet(i);
     52  1.1.2.2  martin 	npf_cache_t *npc = get_cached_pkt(m, NULL);
     53  1.1.2.2  martin 	npf_conn_t *con;
     54  1.1.2.2  martin 
     55  1.1.2.2  martin 	con = npf_conn_establish(npc, PFIL_IN, true);
     56  1.1.2.2  martin 	CHECK_TRUE(con != NULL);
     57  1.1.2.2  martin 	if (expire) {
     58  1.1.2.2  martin 		npf_conn_expire(con);
     59  1.1.2.2  martin 	}
     60  1.1.2.2  martin 	npf_conn_release(con);
     61  1.1.2.2  martin 	put_cached_pkt(npc);
     62  1.1.2.2  martin 	return true;
     63  1.1.2.2  martin }
     64  1.1.2.2  martin 
     65  1.1.2.2  martin static bool
     66  1.1.2.2  martin run_conn_gc(unsigned active, unsigned expired, unsigned expected)
     67  1.1.2.2  martin {
     68  1.1.2.2  martin 	npf_t *npf = npf_getkernctx();
     69  1.1.2.2  martin 	npf_conndb_t *cd = npf_conndb_create();
     70  1.1.2.2  martin 	unsigned total, n = 0;
     71  1.1.2.2  martin 
     72  1.1.2.2  martin 	npf->conn_db = cd;
     73  1.1.2.2  martin 
     74  1.1.2.2  martin 	/*
     75  1.1.2.2  martin 	 * Insert the given number of active and expired connections..
     76  1.1.2.2  martin 	 */
     77  1.1.2.2  martin 	total = active + expired;
     78  1.1.2.2  martin 
     79  1.1.2.2  martin 	while (active || expired) {
     80  1.1.2.2  martin 		if (active) {
     81  1.1.2.2  martin 			enqueue_connection(n++, false);
     82  1.1.2.2  martin 			active--;
     83  1.1.2.2  martin 		}
     84  1.1.2.2  martin 		if (expired) {
     85  1.1.2.2  martin 			enqueue_connection(n++, true);
     86  1.1.2.2  martin 			expired--;
     87  1.1.2.2  martin 		}
     88  1.1.2.2  martin 	}
     89  1.1.2.2  martin 
     90  1.1.2.2  martin 	/* Verify the count. */
     91  1.1.2.2  martin 	n = count_conns(cd);
     92  1.1.2.2  martin 	CHECK_TRUE(n == total);
     93  1.1.2.2  martin 
     94  1.1.2.2  martin 	/*
     95  1.1.2.2  martin 	 * Run G/C.  Check the remaining.
     96  1.1.2.2  martin 	 */
     97  1.1.2.2  martin 	npf_conndb_gc(npf, cd, false, false);
     98  1.1.2.2  martin 	n = count_conns(cd);
     99  1.1.2.2  martin 	if (lverbose) {
    100  1.1.2.2  martin 		printf("in conndb -- %u (expected %u)\n", n, expected);
    101  1.1.2.2  martin 	}
    102  1.1.2.2  martin 	CHECK_TRUE(n == expected);
    103  1.1.2.2  martin 
    104  1.1.2.2  martin 	/* Flush and destroy. */
    105  1.1.2.2  martin 	npf_conndb_gc(npf, cd, true, false);
    106  1.1.2.2  martin 	npf_conndb_destroy(cd);
    107  1.1.2.2  martin 	npf->conn_db = NULL;
    108  1.1.2.2  martin 	return true;
    109  1.1.2.2  martin }
    110  1.1.2.2  martin 
    111  1.1.2.2  martin static bool
    112  1.1.2.2  martin run_gc_tests(void)
    113  1.1.2.2  martin {
    114  1.1.2.2  martin 	bool ok;
    115  1.1.2.2  martin 	int val;
    116  1.1.2.2  martin 
    117  1.1.2.2  martin 	/* Check the default value. */
    118  1.1.2.2  martin 	npfk_param_get(npf_getkernctx(), "gc.step", &val);
    119  1.1.2.2  martin 	CHECK_TRUE(val == 256);
    120  1.1.2.2  martin 
    121  1.1.2.2  martin 	/* Empty => GC => 0 in conndb. */
    122  1.1.2.2  martin 	ok = run_conn_gc(0, 0, 0);
    123  1.1.2.2  martin 	CHECK_TRUE(ok);
    124  1.1.2.2  martin 
    125  1.1.2.2  martin 	/* 1 active => GC => 1 in conndb. */
    126  1.1.2.2  martin 	ok = run_conn_gc(1, 0, 1);
    127  1.1.2.2  martin 	CHECK_TRUE(ok);
    128  1.1.2.2  martin 
    129  1.1.2.2  martin 	/* 1 expired => GC => 0 in conndb. */
    130  1.1.2.2  martin 	ok = run_conn_gc(0, 1, 0);
    131  1.1.2.2  martin 	CHECK_TRUE(ok);
    132  1.1.2.2  martin 
    133  1.1.2.2  martin 	/* 1 active and 1 expired => GC => 1 in conndb. */
    134  1.1.2.2  martin 	ok = run_conn_gc(1, 1, 1);
    135  1.1.2.2  martin 	CHECK_TRUE(ok);
    136  1.1.2.2  martin 
    137  1.1.2.2  martin 	/* 2 expired => GC => 0 in conndb. */
    138  1.1.2.2  martin 	ok = run_conn_gc(0, 2, 0);
    139  1.1.2.2  martin 	CHECK_TRUE(ok);
    140  1.1.2.2  martin 
    141  1.1.2.2  martin 	/* 128 expired => GC => 0 in conndb. */
    142  1.1.2.2  martin 	ok = run_conn_gc(0, 128, 0);
    143  1.1.2.2  martin 	CHECK_TRUE(ok);
    144  1.1.2.2  martin 
    145  1.1.2.2  martin 	/* 512 expired => GC => 256 in conndb. */
    146  1.1.2.2  martin 	ok = run_conn_gc(0, 512, 256);
    147  1.1.2.2  martin 	CHECK_TRUE(ok);
    148  1.1.2.2  martin 
    149  1.1.2.2  martin 	/* 512 expired => GC => 127 in conndb. */
    150  1.1.2.2  martin 	npfk_param_set(npf_getkernctx(), "gc.step", 128);
    151  1.1.2.2  martin 	ok = run_conn_gc(0, 512, 384);
    152  1.1.2.2  martin 	CHECK_TRUE(ok);
    153  1.1.2.2  martin 
    154  1.1.2.2  martin 	return true;
    155  1.1.2.2  martin }
    156  1.1.2.2  martin 
    157  1.1.2.2  martin static bool
    158  1.1.2.2  martin run_conndb_tests(npf_t *npf)
    159  1.1.2.2  martin {
    160  1.1.2.2  martin 	npf_conndb_t *orig_cd = npf->conn_db;
    161  1.1.2.2  martin 	bool ok;
    162  1.1.2.2  martin 
    163  1.1.2.2  martin 	npf_config_enter(npf);
    164  1.1.2.2  martin 	npf_conn_tracking(npf, true);
    165  1.1.2.2  martin 	npf_config_exit(npf);
    166  1.1.2.2  martin 
    167  1.1.2.2  martin 	ok = run_gc_tests();
    168  1.1.2.2  martin 
    169  1.1.2.2  martin 	/* We *MUST* restore the valid conndb. */
    170  1.1.2.2  martin 	npf->conn_db = orig_cd;
    171  1.1.2.2  martin 	return ok;
    172  1.1.2.2  martin }
    173  1.1.2.2  martin 
    174  1.1.2.2  martin 
    175  1.1.2.2  martin static void
    176  1.1.2.2  martin worker_test_task(npf_t *npf)
    177  1.1.2.2  martin {
    178  1.1.2.2  martin 	bool *done = atomic_load_acquire(&npf->arg);
    179  1.1.2.2  martin 	atomic_store_release(done, true);
    180  1.1.2.2  martin }
    181  1.1.2.2  martin 
    182  1.1.2.2  martin static bool
    183  1.1.2.2  martin run_worker_tests(npf_t *npf)
    184  1.1.2.2  martin {
    185  1.1.2.2  martin 	unsigned n = 100;
    186  1.1.2.2  martin 	int error;
    187  1.1.2.2  martin 
    188  1.1.2.2  martin 	/* Spawn a worker thread. */
    189  1.1.2.2  martin 	error = npf_worker_sysinit(1);
    190  1.1.2.2  martin 	assert(error == 0);
    191  1.1.2.2  martin 
    192  1.1.2.2  martin 	/*
    193  1.1.2.2  martin 	 * Enlist/discharge an instance, trying to trigger a race.
    194  1.1.2.2  martin 	 */
    195  1.1.2.2  martin 	while (n--) {
    196  1.1.2.2  martin 		bool task_done = false;
    197  1.1.2.2  martin 		unsigned retry = 100;
    198  1.1.2.2  martin 		npf_t *test_npf;
    199  1.1.2.2  martin 
    200  1.1.2.2  martin 		/*
    201  1.1.2.2  martin 		 * Initialize a dummy NPF instance and add a test task.
    202  1.1.2.2  martin 		 * We will (ab)use npf_t::arg here.
    203  1.1.2.2  martin 		 *
    204  1.1.2.2  martin 		 * XXX/TODO: We should use:
    205  1.1.2.2  martin 		 *
    206  1.1.2.2  martin 		 *	npfk_create(NPF_NO_GC, &npftest_mbufops,
    207  1.1.2.2  martin 		 *	    &npftest_ifops, &task_done);
    208  1.1.2.2  martin 		 *
    209  1.1.2.2  martin 		 * However, it resets the interface state and breaks
    210  1.1.2.2  martin 		 * other tests; to be refactor.
    211  1.1.2.2  martin 		 */
    212  1.1.2.2  martin 		test_npf = kmem_zalloc(sizeof(npf_t), KM_SLEEP);
    213  1.1.2.2  martin 		atomic_store_release(&test_npf->arg, &task_done);
    214  1.1.2.2  martin 		test_npf->ebr = npf_ebr_create();
    215  1.1.2.2  martin 
    216  1.1.2.2  martin 		error = npf_worker_addfunc(test_npf, worker_test_task);
    217  1.1.2.2  martin 		assert(error == 0);
    218  1.1.2.2  martin 
    219  1.1.2.2  martin 		/* Enlist the NPF instance. */
    220  1.1.2.2  martin 		npf_worker_enlist(test_npf);
    221  1.1.2.2  martin 
    222  1.1.2.2  martin 		/* Wait for the task to be done. */
    223  1.1.2.2  martin 		while (!atomic_load_acquire(&task_done) && retry--) {
    224  1.1.2.2  martin 			npf_worker_signal(test_npf);
    225  1.1.2.2  martin 			kpause("gctest", false, mstohz(1), NULL);
    226  1.1.2.2  martin 		}
    227  1.1.2.2  martin 
    228  1.1.2.2  martin 		CHECK_TRUE(atomic_load_acquire(&task_done));
    229  1.1.2.2  martin 		npf_worker_discharge(test_npf);
    230  1.1.2.2  martin 
    231  1.1.2.2  martin 		/* Clear the parameter and signal again. */
    232  1.1.2.2  martin 		atomic_store_release(&test_npf->arg, NULL);
    233  1.1.2.2  martin 		npf_worker_signal(test_npf);
    234  1.1.2.2  martin 
    235  1.1.2.2  martin 		npf_ebr_destroy(test_npf->ebr);
    236  1.1.2.2  martin 		kmem_free(test_npf, sizeof(npf_t)); // npfk_destroy()
    237  1.1.2.2  martin 	}
    238  1.1.2.2  martin 
    239  1.1.2.2  martin 	/*
    240  1.1.2.2  martin 	 * Destroy the worker.
    241  1.1.2.2  martin 	 *
    242  1.1.2.2  martin 	 * Attempts to enlist, discharge or signal should have no effect.
    243  1.1.2.2  martin 	 */
    244  1.1.2.2  martin 
    245  1.1.2.2  martin 	npf_worker_sysfini();
    246  1.1.2.2  martin 	npf_worker_enlist(npf);
    247  1.1.2.2  martin 	npf_worker_signal(npf);
    248  1.1.2.2  martin 	npf_worker_discharge(npf);
    249  1.1.2.2  martin 	return true;
    250  1.1.2.2  martin }
    251  1.1.2.2  martin 
    252  1.1.2.2  martin bool
    253  1.1.2.2  martin npf_gc_test(bool verbose)
    254  1.1.2.2  martin {
    255  1.1.2.2  martin 	npf_t *npf = npf_getkernctx();
    256  1.1.2.2  martin 	bool ok;
    257  1.1.2.2  martin 
    258  1.1.2.2  martin 	lverbose = verbose;
    259  1.1.2.2  martin 
    260  1.1.2.2  martin 	ok = run_conndb_tests(npf);
    261  1.1.2.2  martin 	CHECK_TRUE(ok);
    262  1.1.2.2  martin 
    263  1.1.2.2  martin 	ok = run_worker_tests(npf);
    264  1.1.2.2  martin 	CHECK_TRUE(ok);
    265  1.1.2.2  martin 
    266  1.1.2.2  martin 	return ok;
    267  1.1.2.2  martin }
    268