]> git.sur5r.net Git - openldap/blobdiff - servers/slapd/zn_malloc.c
ACIs almost entirely factored out of slapd
[openldap] / servers / slapd / zn_malloc.c
index e276583a851224871938e30556de231548af381d..eb2f035de651b1b99406bf5f592e2a95206cfe55 100644 (file)
@@ -2,7 +2,7 @@
 /* $OpenLDAP$*/
 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
  *
- * Copyright 2003-2004 The OpenLDAP Foundation.
+ * Copyright 2003-2005 The OpenLDAP Foundation.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
 #include <stdio.h>
 #include <ac/string.h>
 #include <sys/types.h>
-#include <sys/mman.h>
 #include <fcntl.h>
 
 #include "slap.h"
 
 #ifdef SLAP_ZONE_ALLOC
 
+#include <sys/mman.h>
+
 static int slap_zone_cmp(const void *v1, const void *v2);
 void * slap_replenish_zopool(void *ctx);
 
@@ -62,26 +63,29 @@ slap_zn_mem_destroy(
        } while (pad_shift >>= 1);
 
        ldap_pvt_thread_mutex_lock( &zh->zh_mutex );
-       for (i = 0; i <= zh->zh_zoneorder - order_start; i++) {
+       for (i = 0; i < zh->zh_zoneorder - order_start + 1; i++) {
                zo = LDAP_LIST_FIRST(&zh->zh_free[i]);
                while (zo) {
                        struct zone_object *zo_tmp = zo;
                        zo = LDAP_LIST_NEXT(zo, zo_link);
+                       LDAP_LIST_REMOVE(zo_tmp, zo_link);
                        LDAP_LIST_INSERT_HEAD(&zh->zh_zopool, zo_tmp, zo_link);
                }
        }
        ch_free(zh->zh_free);
 
        for (i = 0; i < zh->zh_numzones; i++) {
-               for (j = 0; j < zh->zh_zoneorder - order_start; j++) {
+               for (j = 0; j < zh->zh_zoneorder - order_start + 1; j++) {
                        ch_free(zh->zh_maps[i][j]);
                }
                ch_free(zh->zh_maps[i]);
                munmap(zh->zh_zones[i], zh->zh_zonesize);
+               ldap_pvt_thread_rdwr_destroy(&zh->zh_znlock[i]);
        }
        ch_free(zh->zh_maps);
        ch_free(zh->zh_zones);
        ch_free(zh->zh_seqno);
+       ch_free(zh->zh_znlock);
 
        avl_free(zh->zh_zonetree, slap_zo_release);
 
@@ -99,8 +103,9 @@ slap_zn_mem_destroy(
                zo = LDAP_LIST_NEXT(zo, zo_link);
                ch_free(zo_tmp);
        }
-       ldap_pvt_thread_mutex_unlock( &zh->zh_mutex );
-       ldap_pvt_thread_mutex_destroy( &zh->zh_mutex );
+       ldap_pvt_thread_mutex_unlock(&zh->zh_mutex);
+       ldap_pvt_thread_rdwr_destroy(&zh->zh_lock);
+       ldap_pvt_thread_mutex_destroy(&zh->zh_mutex);
        ch_free(zh);
 }
 
@@ -120,6 +125,13 @@ slap_zn_mem_create(
        int i, j;
        struct zone_object *zo;
 
+       Debug(LDAP_DEBUG_NONE,
+               "--> slap_zn_mem_create: initsize=%d, maxsize=%d\n",
+               initsize, maxsize, 0);
+       Debug(LDAP_DEBUG_NONE,
+               "++> slap_zn_mem_create: deltasize=%d, zonesize=%d\n",
+               deltasize, zonesize, 0);
+
        zh = (struct zone_heap *)ch_calloc(1, sizeof(struct zone_heap));
 
        zh->zh_fd = open("/dev/zero", O_RDWR);
@@ -158,6 +170,8 @@ slap_zn_mem_create(
        order = order_end - order_start + 1;
 
        zh->zh_zones = (void **)ch_malloc(zh->zh_maxzones * sizeof(void*));
+       zh->zh_znlock = (ldap_pvt_thread_rdwr_t *)ch_malloc(
+                                               zh->zh_maxzones * sizeof(ldap_pvt_thread_rdwr_t *));
        zh->zh_maps = (unsigned char ***)ch_malloc(
                                        zh->zh_maxzones * sizeof(unsigned char**));
 
@@ -180,7 +194,7 @@ slap_zn_mem_create(
                        int shiftamt = order_start + 1 + j;
                        int nummaps = zh->zh_zonesize >> shiftamt;
                        assert(nummaps);
-                       nummaps /= 8;
+                       nummaps >>= 3;
                        if (!nummaps) nummaps = 1;
                        zh->zh_maps[i][j] = (unsigned char *)ch_malloc(nummaps);
                        memset(zh->zh_maps[i][j], 0, nummaps);
@@ -200,12 +214,16 @@ slap_zn_mem_create(
                }
                zo = LDAP_LIST_FIRST(&zh->zh_zopool);
                LDAP_LIST_REMOVE(zo, zo_link);
-               zo->zo_ptr = (void*)((unsigned long)zh->zh_zones[i]>>zh->zh_zoneorder);
+               zo->zo_ptr = zh->zh_zones[i];
+               zo->zo_siz = zh->zh_zonesize;
                zo->zo_idx = i;
                avl_insert(&zh->zh_zonetree, zo, slap_zone_cmp, avl_dup_error);
+               ldap_pvt_thread_rdwr_init(&zh->zh_znlock[i]);
        }
 
+       LDAP_STAILQ_INIT(&zh->zh_latency_history_queue);
        ldap_pvt_thread_mutex_init(&zh->zh_mutex);
+       ldap_pvt_thread_rdwr_init(&zh->zh_lock);
 
        return zh;
 }
@@ -226,6 +244,9 @@ slap_zn_malloc(
        unsigned long diff;
        int i, j, k;
 
+       Debug(LDAP_DEBUG_NONE,
+               "--> slap_zn_malloc: size=%d\n", size, 0, 0);
+
        if (!zh) return ber_memalloc_x(size, NULL);
 
        /* round up to doubleword boundary */
@@ -262,6 +283,8 @@ retry:
                zo_new->zo_idx = -1;
                LDAP_LIST_INSERT_HEAD(&zh->zh_zopool, zo_new, zo_link);
                ldap_pvt_thread_mutex_unlock( &zh->zh_mutex );
+               Debug(LDAP_DEBUG_NONE, "slap_zn_malloc: returning 0x%x, 0x%x\n",
+                               ptr, (int)ptr>>(zh->zh_zoneorder+1), 0);
                return((void*)ptr);
        } else if (i <= zh->zh_zoneorder) {
                for (j = i; j > order; j--) {
@@ -274,6 +297,9 @@ retry:
                        LDAP_LIST_REMOVE(zo_right, zo_link);
                        zo_right->zo_ptr = zo_left->zo_ptr + (1 << j);
                        zo_right->zo_idx = zo_left->zo_idx;
+                       Debug(LDAP_DEBUG_NONE,
+                               "slap_zn_malloc: split (left=0x%x, right=0x%x)\n",
+                               zo_left->zo_ptr, zo_right->zo_ptr, 0);
                        if (j == order + 1) {
                                ptr = zo_left->zo_ptr;
                                diff = (unsigned long)((char*)ptr -
@@ -286,6 +312,9 @@ retry:
                                                &zh->zh_free[j-1-order_start], zo_right, zo_link);
                                LDAP_LIST_INSERT_HEAD(&zh->zh_zopool, zo_left, zo_link);
                                ldap_pvt_thread_mutex_unlock( &zh->zh_mutex );
+                               Debug(LDAP_DEBUG_NONE,
+                                       "slap_zn_malloc: returning 0x%x, 0x%x\n",
+                                       ptr, (int)ptr>>(zh->zh_zoneorder+1), 0);
                                return((void*)ptr);
                        } else {
                                LDAP_LIST_INSERT_HEAD(
@@ -302,6 +331,9 @@ retry:
                        Debug( LDAP_DEBUG_TRACE,
                                "slap_zn_malloc of %lu bytes failed, using ch_malloc\n",
                                (long)size, 0, 0);
+                       Debug(LDAP_DEBUG_NONE,
+                               "slap_zn_malloc: returning 0x%x, 0x%x\n",
+                               ptr, (int)ptr>>(zh->zh_zoneorder+1), 0);
                        return (void*)ch_malloc(size);
                }
 
@@ -309,13 +341,13 @@ retry:
                        zh->zh_zones[i] = mmap(0, zh->zh_zonesize, PROT_READ | PROT_WRITE,
                                                                MAP_PRIVATE, zh->zh_fd, 0);
                        zh->zh_maps[i] = (unsigned char **)
-                                               ch_malloc((zh->zh_zoneorder - order_start +1) *
+                                               ch_malloc((zh->zh_zoneorder - order_start + 1) *
                                                sizeof(unsigned char *));
-                       for (j = 0; j < order; j++) {
+                       for (j = 0; j < zh->zh_zoneorder-order_start+1; j++) {
                                int shiftamt = order_start + 1 + j;
                                int nummaps = zh->zh_zonesize >> shiftamt;
                                assert(nummaps);
-                               nummaps /= 8;
+                               nummaps >>= 3;
                                if (!nummaps) nummaps = 1;
                                zh->zh_maps[i][j] = (unsigned char *)ch_malloc(nummaps);
                                memset(zh->zh_maps[i][j], 0, nummaps);
@@ -336,10 +368,11 @@ retry:
                        }
                        zo = LDAP_LIST_FIRST(&zh->zh_zopool);
                        LDAP_LIST_REMOVE(zo, zo_link);
-                       zo->zo_ptr = (void*)((unsigned long)zh->zh_zones[i]>>
-                                               zh->zh_zoneorder);
+                       zo->zo_ptr = zh->zh_zones[i];
+                       zo->zo_siz = zh->zh_zonesize;
                        zo->zo_idx = i;
                        avl_insert(&zh->zh_zonetree, zo, slap_zone_cmp, avl_dup_error);
+                       ldap_pvt_thread_rdwr_init(&zh->zh_znlock[i]);
                }
                zh->zh_numzones += zh->zh_deltazones;
                ldap_pvt_thread_mutex_unlock( &zh->zh_mutex );
@@ -372,10 +405,13 @@ slap_zn_realloc(void *ptr, ber_len_t size, void *ctx)
        void *newptr = NULL;
        struct zone_heap *zone = NULL;
 
+       Debug(LDAP_DEBUG_NONE,
+               "--> slap_zn_realloc: ptr=0x%x, size=%d\n", ptr, size, 0);
+
        if (ptr == NULL)
                return slap_zn_malloc(size, zh);
 
-       zoi.zo_ptr = (void*)((unsigned long)p >> zh->zh_zoneorder);
+       zoi.zo_ptr = p;
        zoi.zo_idx = -1;
 
        if (zh) {
@@ -429,9 +465,11 @@ slap_zn_free(void *ptr, void *ctx)
        int i, k, inserted = 0, idx;
        struct zone_heap *zone = NULL;
 
-       zoi.zo_ptr = (void*)((unsigned long)p >> zh->zh_zoneorder);
+       zoi.zo_ptr = p;
        zoi.zo_idx = -1;
 
+       Debug(LDAP_DEBUG_NONE, "--> slap_zn_free: ptr=0x%x\n", ptr, 0, 0);
+
        if (zh) {
                ldap_pvt_thread_mutex_lock( &zh->zh_mutex );
                zoo = avl_find(zh->zh_zonetree, &zoi, slap_zone_cmp);
@@ -479,6 +517,9 @@ slap_zn_free(void *ptr, void *ctx)
                                                if (i < zh->zh_zoneorder) {
                                                        inserted = 1;
                                                        zo->zo_ptr = tmpp;
+                                                       Debug(LDAP_DEBUG_NONE,
+                                                               "slap_zn_free: merging 0x%x\n",
+                                                               zo->zo_ptr, 0, 0);
                                                        LDAP_LIST_INSERT_HEAD(&zh->zh_free[i-order_start+1],
                                                                        zo, zo_link);
                                                }
@@ -490,6 +531,10 @@ slap_zn_free(void *ptr, void *ctx)
                                                zo = LDAP_LIST_FIRST(&zh->zh_zopool);
                                                LDAP_LIST_REMOVE(zo, zo_link);
                                                zo->zo_ptr = tmpp;
+                                               zo->zo_idx = idx;
+                                               Debug(LDAP_DEBUG_NONE,
+                                                       "slap_zn_free: merging 0x%x\n",
+                                                       zo->zo_ptr, 0, 0);
                                                LDAP_LIST_INSERT_HEAD(&zh->zh_free[i-order_start],
                                                                zo, zo_link);
                                                break;
@@ -497,7 +542,7 @@ slap_zn_free(void *ptr, void *ctx)
                                                Debug(LDAP_DEBUG_ANY, "slap_zn_free: "
                                                        "free object not found while bit is clear.\n",
                                                        0, 0, 0);
-                                               assert(zo);
+                                               assert(zo != NULL);
 
                                        }
                                } else {
@@ -508,6 +553,10 @@ slap_zn_free(void *ptr, void *ctx)
                                                zo = LDAP_LIST_FIRST(&zh->zh_zopool);
                                                LDAP_LIST_REMOVE(zo, zo_link);
                                                zo->zo_ptr = tmpp;
+                                               zo->zo_idx = idx;
+                                               Debug(LDAP_DEBUG_NONE,
+                                                       "slap_zn_free: merging 0x%x\n",
+                                                       zo->zo_ptr, 0, 0);
                                                LDAP_LIST_INSERT_HEAD(&zh->zh_free[i-order_start],
                                                                zo, zo_link);
                                        }
@@ -530,6 +579,9 @@ slap_zn_free(void *ptr, void *ctx)
                                        if (zo) {
                                                if (i < zh->zh_zoneorder) {
                                                        inserted = 1;
+                                                       Debug(LDAP_DEBUG_NONE,
+                                                               "slap_zn_free: merging 0x%x\n",
+                                                               zo->zo_ptr, 0, 0);
                                                        LDAP_LIST_INSERT_HEAD(&zh->zh_free[i-order_start+1],
                                                                        zo, zo_link);
                                                        continue;
@@ -541,6 +593,10 @@ slap_zn_free(void *ptr, void *ctx)
                                                zo = LDAP_LIST_FIRST(&zh->zh_zopool);
                                                LDAP_LIST_REMOVE(zo, zo_link);
                                                zo->zo_ptr = tmpp;
+                                               zo->zo_idx = idx;
+                                               Debug(LDAP_DEBUG_NONE,
+                                                       "slap_zn_free: merging 0x%x\n",
+                                                       zo->zo_ptr, 0, 0);
                                                LDAP_LIST_INSERT_HEAD(&zh->zh_free[i-order_start],
                                                                zo, zo_link);
                                                break;
@@ -548,7 +604,7 @@ slap_zn_free(void *ptr, void *ctx)
                                                Debug(LDAP_DEBUG_ANY, "slap_zn_free: "
                                                        "free object not found while bit is clear.\n",
                                                        0, 0, 0 );
-                                               assert( zo );
+                                               assert(zo != NULL);
 
                                        }
                                } else {
@@ -559,6 +615,10 @@ slap_zn_free(void *ptr, void *ctx)
                                                zo = LDAP_LIST_FIRST(&zh->zh_zopool);
                                                LDAP_LIST_REMOVE(zo, zo_link);
                                                zo->zo_ptr = tmpp;
+                                               zo->zo_idx = idx;
+                                               Debug(LDAP_DEBUG_NONE,
+                                                       "slap_zn_free: merging 0x%x\n",
+                                                       zo->zo_ptr, 0, 0);
                                                LDAP_LIST_INSERT_HEAD(&zh->zh_free[i-order_start],
                                                                zo, zo_link);
                                        }
@@ -575,8 +635,15 @@ slap_zone_cmp(const void *v1, const void *v2)
 {
        const struct zone_object *zo1 = v1;
        const struct zone_object *zo2 = v2;
+       char *ptr1;
+       char *ptr2;
+       ber_len_t zpad;
 
-       return zo1->zo_ptr - zo2->zo_ptr;
+       zpad = zo2->zo_siz - 1;
+       ptr1 = (char*)(((unsigned long)zo1->zo_ptr + zpad) & ~zpad);
+       ptr2 = (char*)zo2->zo_ptr + ((char*)ptr1 - (char*)zo1->zo_ptr);
+       ptr2 = (char*)(((unsigned long)ptr2 + zpad) & ~zpad);
+       return (int)((char*)ptr1 - (char*)ptr2);
 }
 
 void *
@@ -604,4 +671,302 @@ slap_replenish_zopool(
 
        return zo_block;
 }
+
+int
+slap_zn_invalidate(
+       void *ctx,
+       void *ptr
+)
+{
+       struct zone_heap* zh = ctx;
+       struct zone_object zoi, *zoo;
+       struct zone_heap *zone = NULL;
+       int seqno = *((ber_len_t*)ptr - 2);
+       int idx = -1, rc = 0;
+       int pad = 2*sizeof(int)-1, pad_shift;
+       int order_start = -1, i;
+       struct zone_object *zo;
+
+       pad_shift = pad - 1;
+       do {
+               order_start++;
+       } while (pad_shift >>= 1);
+
+       zoi.zo_ptr = ptr;
+       zoi.zo_idx = -1;
+
+       ldap_pvt_thread_mutex_lock( &zh->zh_mutex );
+       zoo = avl_find(zh->zh_zonetree, &zoi, slap_zone_cmp);
+
+       if (zoo) {
+               idx = zoo->zo_idx;
+               assert(idx != -1);
+               madvise(zh->zh_zones[idx], zh->zh_zonesize, MADV_DONTNEED);
+               for (i = 0; i < zh->zh_zoneorder - order_start + 1; i++) {
+                       int shiftamt = order_start + 1 + i;
+                       int nummaps = zh->zh_zonesize >> shiftamt;
+                       assert(nummaps);
+                       nummaps >>= 3;
+                       if (!nummaps) nummaps = 1;
+                       memset(zh->zh_maps[idx][i], 0, nummaps);
+                       zo = LDAP_LIST_FIRST(&zh->zh_free[i]);
+                       while (zo) {
+                               struct zone_object *zo_tmp = zo;
+                               zo = LDAP_LIST_NEXT(zo, zo_link);
+                               if (zo_tmp && zo_tmp->zo_idx == idx) {
+                                       LDAP_LIST_REMOVE(zo_tmp, zo_link);
+                                       LDAP_LIST_INSERT_HEAD(&zh->zh_zopool, zo_tmp, zo_link);
+                               }
+                       }
+               }
+               if (LDAP_LIST_EMPTY(&zh->zh_zopool)) {
+                       slap_replenish_zopool(zh);
+               }
+               zo = LDAP_LIST_FIRST(&zh->zh_zopool);
+               LDAP_LIST_REMOVE(zo, zo_link);
+               zo->zo_ptr = zh->zh_zones[idx];
+               zo->zo_idx = idx;
+               LDAP_LIST_INSERT_HEAD(&zh->zh_free[zh->zh_zoneorder-order_start],
+                                                               zo, zo_link);
+               zh->zh_seqno[idx]++;
+       } else {
+               Debug(LDAP_DEBUG_NONE, "zone not found for (ctx=0x%x, ptr=0x%x) !\n",
+                               ctx, ptr, 0);
+       }
+
+       ldap_pvt_thread_mutex_unlock( &zh->zh_mutex );
+       Debug(LDAP_DEBUG_NONE, "zone %d invalidate\n", idx, 0, 0);
+       return rc;
+}
+
+int
+slap_zn_validate(
+       void *ctx,
+       void *ptr,
+       int seqno
+)
+{
+       struct zone_heap* zh = ctx;
+       struct zone_object zoi, *zoo;
+       struct zone_heap *zone = NULL;
+       int idx, rc = 0;
+
+       zoi.zo_ptr = ptr;
+       zoi.zo_idx = -1;
+
+       zoo = avl_find(zh->zh_zonetree, &zoi, slap_zone_cmp);
+
+       if (zoo) {
+               idx = zoo->zo_idx;
+               assert(idx != -1);
+               assert(seqno <= zh->zh_seqno[idx]);
+               rc = (seqno == zh->zh_seqno[idx]);
+       }
+
+       return rc;
+}
+
+int slap_zh_rlock(
+       void *ctx
+)
+{
+       struct zone_heap* zh = ctx;
+       ldap_pvt_thread_rdwr_rlock(&zh->zh_lock);
+}
+
+int slap_zh_runlock(
+       void *ctx
+)
+{
+       struct zone_heap* zh = ctx;
+       ldap_pvt_thread_rdwr_runlock(&zh->zh_lock);
+}
+
+int slap_zh_wlock(
+       void *ctx
+)
+{
+       struct zone_heap* zh = ctx;
+       ldap_pvt_thread_rdwr_wlock(&zh->zh_lock);
+}
+
+int slap_zh_wunlock(
+       void *ctx
+)
+{
+       struct zone_heap* zh = ctx;
+       ldap_pvt_thread_rdwr_wunlock(&zh->zh_lock);
+}
+
+int slap_zn_rlock(
+       void *ctx,
+       void *ptr
+)
+{
+       struct zone_heap* zh = ctx;
+       struct zone_object zoi, *zoo;
+       struct zone_heap *zone = NULL;
+       int idx;
+
+       zoi.zo_ptr = ptr;
+       zoi.zo_idx = -1;
+
+       ldap_pvt_thread_mutex_lock( &zh->zh_mutex );
+       zoo = avl_find(zh->zh_zonetree, &zoi, slap_zone_cmp);
+       ldap_pvt_thread_mutex_unlock( &zh->zh_mutex );
+
+       if (zoo) {
+               idx = zoo->zo_idx;
+               assert(idx != -1);
+               ldap_pvt_thread_rdwr_rlock(&zh->zh_znlock[idx]);
+       }
+}
+
+int slap_zn_runlock(
+       void *ctx,
+       void *ptr
+)
+{
+       struct zone_heap* zh = ctx;
+       struct zone_object zoi, *zoo;
+       struct zone_heap *zone = NULL;
+       int idx;
+
+       zoi.zo_ptr = ptr;
+       zoi.zo_idx = -1;
+
+       ldap_pvt_thread_mutex_lock( &zh->zh_mutex );
+       zoo = avl_find(zh->zh_zonetree, &zoi, slap_zone_cmp);
+       ldap_pvt_thread_mutex_unlock( &zh->zh_mutex );
+
+       if (zoo) {
+               idx = zoo->zo_idx;
+               assert(idx != -1);
+               ldap_pvt_thread_rdwr_runlock(&zh->zh_znlock[idx]);
+       }
+}
+
+int slap_zn_wlock(
+       void *ctx,
+       void *ptr
+)
+{
+       struct zone_heap* zh = ctx;
+       struct zone_object zoi, *zoo;
+       struct zone_heap *zone = NULL;
+       int idx;
+
+       zoi.zo_ptr = ptr;
+       zoi.zo_idx = -1;
+
+       ldap_pvt_thread_mutex_lock( &zh->zh_mutex );
+       zoo = avl_find(zh->zh_zonetree, &zoi, slap_zone_cmp);
+       ldap_pvt_thread_mutex_unlock( &zh->zh_mutex );
+
+       if (zoo) {
+               idx = zoo->zo_idx;
+               assert(idx != -1);
+               ldap_pvt_thread_rdwr_wlock(&zh->zh_znlock[idx]);
+       }
+}
+
+int slap_zn_wunlock(
+       void *ctx,
+       void *ptr
+)
+{
+       struct zone_heap* zh = ctx;
+       struct zone_object zoi, *zoo;
+       struct zone_heap *zone = NULL;
+       int idx;
+
+       zoi.zo_ptr = ptr;
+       zoi.zo_idx = -1;
+
+       ldap_pvt_thread_mutex_lock( &zh->zh_mutex );
+       zoo = avl_find(zh->zh_zonetree, &zoi, slap_zone_cmp);
+       ldap_pvt_thread_mutex_unlock( &zh->zh_mutex );
+
+       if (zoo) {
+               idx = zoo->zo_idx;
+               assert(idx != -1);
+               ldap_pvt_thread_rdwr_wunlock(&zh->zh_znlock[idx]);
+       }
+}
+
+#define T_SEC_IN_USEC 1000000
+
+static int
+slap_timediff(struct timeval *tv_begin, struct timeval *tv_end)
+{
+       uint64_t t_begin, t_end, t_diff;
+
+       t_begin = T_SEC_IN_USEC * tv_begin->tv_sec + tv_begin->tv_usec;
+       t_end  = T_SEC_IN_USEC * tv_end->tv_sec  + tv_end->tv_usec;
+       t_diff  = t_end - t_begin;
+       
+       if ( t_diff < 0 )
+               t_diff = 0;
+
+       return (int)t_diff;
+}
+
+void
+slap_set_timing(struct timeval *tv_set)
+{
+       gettimeofday(tv_set, (struct timezone *)NULL);
+}
+
+int
+slap_measure_timing(struct timeval *tv_set, struct timeval *tv_measure)
+{
+       gettimeofday(tv_measure, (struct timezone *)NULL);
+       return(slap_timediff(tv_set, tv_measure));
+}
+
+#define EMA_WEIGHT 0.999000
+#define SLAP_ZN_LATENCY_HISTORY_QLEN 500
+int
+slap_zn_latency_history(void* ctx, int ea_latency)
+{
+/* TODO: monitor /proc/stat (swap) as well */
+       struct zone_heap* zh = ctx;
+       double t_diff = 0.0;
+
+       zh->zh_ema_latency = (double)ea_latency * (1.0 - EMA_WEIGHT)
+                                       + zh->zh_ema_latency * EMA_WEIGHT;
+       if (!zh->zh_swapping && zh->zh_ema_samples++ % 100 == 99) {
+               struct zone_latency_history *zlh_entry;
+               zlh_entry = ch_calloc(1, sizeof(struct zone_latency_history));
+               zlh_entry->zlh_latency = zh->zh_ema_latency;
+               LDAP_STAILQ_INSERT_TAIL(
+                               &zh->zh_latency_history_queue, zlh_entry, zlh_next);
+               zh->zh_latency_history_qlen++;
+               while (zh->zh_latency_history_qlen > SLAP_ZN_LATENCY_HISTORY_QLEN) {
+                       struct zone_latency_history *zlh;
+                       zlh = LDAP_STAILQ_FIRST(&zh->zh_latency_history_queue);
+                       LDAP_STAILQ_REMOVE_HEAD(
+                                       &zh->zh_latency_history_queue, zlh_next);
+                       zh->zh_latency_history_qlen--;
+                       ch_free(zlh);
+               }
+               if (zh->zh_latency_history_qlen == SLAP_ZN_LATENCY_HISTORY_QLEN) {
+                       struct zone_latency_history *zlh_first, *zlh_last;
+                       zlh_first = LDAP_STAILQ_FIRST(&zh->zh_latency_history_queue);
+                       zlh_last = LDAP_STAILQ_LAST(&zh->zh_latency_history_queue,
+                                               zone_latency_history, zlh_next);
+                       t_diff = zlh_last->zlh_latency - zlh_first->zlh_latency;
+               }
+               if (t_diff >= 2000) {
+                       zh->zh_latency_jump++;
+               } else {
+                       zh->zh_latency_jump = 0;
+               }
+               if (zh->zh_latency_jump > 3) {
+                       zh->zh_latency_jump = 0;
+                       zh->zh_swapping = 1;
+               }
+       }
+       return zh->zh_swapping;
+}
 #endif /* SLAP_ZONE_ALLOC */