Commit 78172aa0 authored by Howard Chu's avatar Howard Chu
Browse files

Entry/Attribute struct caching, to minimize malloc fragmentation

Note: this breaks LDAP_COMP_MATCH and SLAP_ZONE_MALLOC. But they
were probably broken already anyway.
parent 8ae4cbe6
......@@ -69,7 +69,7 @@ do_add( Operation *op, SlapReply *rs )
return SLAPD_DISCONNECT;
}
op->ora_e = (Entry *) ch_calloc( 1, sizeof(Entry) );
op->ora_e = entry_alloc();
rs->sr_err = dnPrettyNormal( NULL, &dn, &op->o_req_dn, &op->o_req_ndn,
op->o_tmpmemctx );
......@@ -506,10 +506,7 @@ slap_mods2entry(
}
}
attr = ch_calloc( 1, sizeof(Attribute) );
/* move ad to attr structure */
attr->a_desc = mods->sml_desc;
attr = attr_alloc( mods->sml_desc );
/* move values to attr structure */
/* should check for duplicates */
......
......@@ -40,37 +40,118 @@
#include "slap.h"
/*
* Allocate in chunks, minimum of 1000 at a time.
*/
#define CHUNK_SIZE 1000
typedef struct slap_list {
struct slap_list *next;
} slap_list;
static slap_list *attr_chunks;
static Attribute *attr_list;
static ldap_pvt_thread_mutex_t attr_mutex;
int
attr_prealloc( int num )
{
Attribute *a;
slap_list *s;
if (!num) return 0;
s = ch_calloc( 1, sizeof(slap_list) + num * sizeof(Attribute));
s->next = attr_chunks;
attr_chunks = s;
a = (Attribute *)(s+1);
for ( ;num>1; num--) {
a->a_next = a+1;
a++;
}
a->a_next = attr_list;
attr_list = (Attribute *)(s+1);
return 0;
}
Attribute *
attr_alloc( AttributeDescription *ad )
{
Attribute *a = ch_malloc( sizeof(Attribute) );
a->a_desc = ad;
Attribute *a;
ldap_pvt_thread_mutex_lock( &attr_mutex );
if ( !attr_list )
attr_prealloc( CHUNK_SIZE );
a = attr_list;
attr_list = a->a_next;
a->a_next = NULL;
a->a_flags = 0;
a->a_vals = NULL;
a->a_nvals = NULL;
#ifdef LDAP_COMP_MATCH
a->a_comp_data = NULL;
#endif
ldap_pvt_thread_mutex_unlock( &attr_mutex );
a->a_desc = ad;
return a;
}
/* Return a list of num attrs */
Attribute *
attrs_alloc( int num )
{
Attribute *head = NULL;
Attribute **a;
ldap_pvt_thread_mutex_lock( &attr_mutex );
for ( a = &attr_list; *a && num > 0; a = &(*a)->a_next ) {
if ( !head )
head = *a;
num--;
}
attr_list = *a;
if ( num > 0 ) {
attr_prealloc( num > CHUNK_SIZE ? num : CHUNK_SIZE );
*a = attr_list;
for ( ; *a && num > 0; a = &(*a)->a_next ) {
if ( !head )
head = *a;
num--;
}
attr_list = *a;
}
*a = NULL;
ldap_pvt_thread_mutex_unlock( &attr_mutex );
return head;
}
void
attr_free( Attribute *a )
{
if ( a->a_nvals && a->a_nvals != a->a_vals ) {
ber_bvarray_free( a->a_nvals );
if ( a->a_nvals && a->a_nvals != a->a_vals &&
!( a->a_flags & SLAP_ATTR_DONT_FREE_VALS )) {
if ( a->a_flags & SLAP_ATTR_DONT_FREE_DATA ) {
free( a->a_nvals );
} else {
ber_bvarray_free( a->a_nvals );
}
}
/* a_vals may be equal to slap_dummy_bv, a static empty berval;
* this is used as a placeholder for attributes that do not carry
* values, e.g. when proxying search entries with the "attrsonly"
* bit set. */
if ( a->a_vals != &slap_dummy_bv ) {
ber_bvarray_free( a->a_vals );
if ( a->a_vals != &slap_dummy_bv &&
!( a->a_flags & SLAP_ATTR_DONT_FREE_VALS )) {
if ( a->a_flags & SLAP_ATTR_DONT_FREE_DATA ) {
free( a->a_vals );
} else {
ber_bvarray_free( a->a_vals );
}
}
free( a );
memset( a, 0, sizeof( Attribute ));
ldap_pvt_thread_mutex_lock( &attr_mutex );
a->a_next = attr_list;
attr_list = a;
ldap_pvt_thread_mutex_unlock( &attr_mutex );
}
#ifdef LDAP_COMP_MATCH
......@@ -399,3 +480,22 @@ attr_delete(
return LDAP_NO_SUCH_ATTRIBUTE;
}
int
attr_init( void )
{
ldap_pvt_thread_mutex_init( &attr_mutex );
return 0;
}
int
attr_destroy( void )
{
slap_list *a;
for ( a=attr_chunks; a; a=attr_chunks ) {
attr_chunks = a->next;
free( a );
}
ldap_pvt_thread_mutex_destroy( &attr_mutex );
return 0;
}
......@@ -176,64 +176,31 @@ int bdb_id2entry_delete(
return rc;
}
#ifdef SLAP_ZONE_ALLOC
int bdb_entry_return(
struct bdb_info *bdb,
Entry *e,
int zseq
)
#else
int bdb_entry_return(
Entry *e
)
#endif
{
#ifdef SLAP_ZONE_ALLOC
if (!slap_zn_validate(bdb->bi_cache.c_zctx, e, zseq)) {
return 0;
}
#endif
/* Our entries are allocated in two blocks; the data comes from
* the db itself and the Entry structure and associated pointers
* are allocated in entry_decode. The db data pointer is saved
* in e_bv. Since the Entry structure is allocated as a single
* block, e_attrs is always a fixed offset from e. The exception
* is when an entry has been modified, in which case we also need
* to free e_attrs.
* in e_bv.
*/
#ifdef LDAP_COMP_MATCH
comp_tree_free( e->e_attrs );
#endif
if( !e->e_bv.bv_val ) { /* Entry added by do_add */
entry_free( e );
return 0;
}
if( (void *) e->e_attrs != (void *) (e+1)) {
attrs_free( e->e_attrs );
}
/* See if the DNs were changed by modrdn */
if( e->e_nname.bv_val < e->e_bv.bv_val || e->e_nname.bv_val >
e->e_bv.bv_val + e->e_bv.bv_len ) {
ch_free(e->e_name.bv_val);
ch_free(e->e_nname.bv_val);
if ( e->e_bv.bv_val ) {
/* See if the DNs were changed by modrdn */
if( e->e_nname.bv_val < e->e_bv.bv_val || e->e_nname.bv_val >
e->e_bv.bv_val + e->e_bv.bv_len ) {
ch_free(e->e_name.bv_val);
ch_free(e->e_nname.bv_val);
}
e->e_name.bv_val = NULL;
e->e_nname.bv_val = NULL;
/* In tool mode the e_bv buffer is realloc'd, leave it alone */
if( !(slapMode & SLAP_TOOL_MODE) ) {
free( e->e_bv.bv_val );
}
BER_BVZERO( &e->e_bv );
}
#ifndef SLAP_ZONE_ALLOC
/* In tool mode the e_bv buffer is realloc'd, leave it alone */
if( !(slapMode & SLAP_TOOL_MODE) ) {
free( e->e_bv.bv_val );
}
#endif /* !SLAP_ZONE_ALLOC */
#ifdef SLAP_ZONE_ALLOC
slap_zn_free( e, bdb->bi_cache.c_zctx );
#else
free( e );
#endif
entry_free( e );
return 0;
}
......
......@@ -424,6 +424,8 @@ bdb_db_open( BackendDB *be )
bdb->bi_flags |= BDB_IS_OPEN;
entry_prealloc( bdb->bi_cache.c_maxsize );
attr_prealloc( bdb->bi_cache.c_maxsize * 20 );
return 0;
fail:
......
......@@ -565,13 +565,10 @@ ldap_build_entry(
slap_syntax_validate_func *validate;
slap_syntax_transform_func *pretty;
attr = (Attribute *)ch_malloc( sizeof( Attribute ) );
attr = attr_alloc( NULL );
if ( attr == NULL ) {
continue;
}
attr->a_flags = 0;
attr->a_next = 0;
attr->a_desc = NULL;
if ( slap_bv2ad( &a, &attr->a_desc, &text )
!= LDAP_SUCCESS )
{
......@@ -792,7 +789,7 @@ retry:
goto cleanup;
}
*ent = ch_calloc( 1, sizeof( Entry ) );
*ent = entry_alloc();
if ( *ent == NULL ) {
rc = LDAP_NO_MEMORY;
goto cleanup;
......
......@@ -1417,7 +1417,7 @@ meta_send_entry(
( void )ber_scanf( &ber, "x" /* [W] */ );
continue;
}
attr = ( Attribute * )ch_calloc( 1, sizeof( Attribute ) );
attr = attr_alloc( NULL );
if ( attr == NULL ) {
continue;
}
......
......@@ -193,7 +193,7 @@ monitor_entry_stub(
if ( rc )
return NULL;
e = ch_calloc( 1, sizeof( Entry ));
e = entry_alloc();
if ( e ) {
struct berval nrdn;
......
......@@ -661,10 +661,10 @@ read_baseObject(
return LDAP_OTHER;
}
bi->sql_baseObject = (Entry *) SLAP_CALLOC( 1, sizeof(Entry) );
bi->sql_baseObject = entry_alloc();
if ( bi->sql_baseObject == NULL ) {
Debug( LDAP_DEBUG_ANY,
"read_baseObject_file: SLAP_CALLOC failed", 0, 0, 0 );
"read_baseObject_file: entry_alloc failed", 0, 0, 0 );
ldif_close( fp );
return LDAP_NO_MEMORY;
}
......
......@@ -615,10 +615,7 @@ backsql_get_attr_vals( void *v_at, void *v_bsi )
append = 1;
/* Make space for the array of values */
attr = (Attribute *) ch_malloc( sizeof( Attribute ) );
attr->a_desc = at->bam_ad;
attr->a_flags = 0;
attr->a_next = NULL;
attr = attr_alloc( at->bam_ad );
attr->a_vals = ch_calloc( count + 1, sizeof( struct berval ) );
if ( attr->a_vals == NULL ) {
Debug( LDAP_DEBUG_TRACE, "Out of memory!\n", 0,0,0 );
......
......@@ -53,8 +53,7 @@ backsql_operational_entryUUID( backsql_info *bi, backsql_entryID *id )
return NULL;
}
a = ch_malloc( sizeof( Attribute ) );
a->a_desc = desc;
a = attr_alloc( desc );
a->a_vals = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
a->a_vals[ 0 ] = val;
......@@ -63,9 +62,6 @@ backsql_operational_entryUUID( backsql_info *bi, backsql_entryID *id )
a->a_nvals = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
a->a_nvals[ 0 ] = nval;
BER_BVZERO( &a->a_nvals[ 1 ] );
a->a_next = NULL;
a->a_flags = 0;
return a;
}
......@@ -77,8 +73,7 @@ backsql_operational_entryCSN( Operation *op )
struct berval entryCSN;
Attribute *a;
a = ch_malloc( sizeof( Attribute ) );
a->a_desc = slap_schema.si_ad_entryCSN;
a = attr_alloc( slap_schema.si_ad_entryCSN );
a->a_vals = ch_malloc( 2 * sizeof( struct berval ) );
BER_BVZERO( &a->a_vals[ 1 ] );
......@@ -100,9 +95,6 @@ backsql_operational_entryCSN( Operation *op )
a->a_nvals = a->a_vals;
a->a_next = NULL;
a->a_flags = 0;
return a;
}
......
......@@ -2469,7 +2469,7 @@ backsql_entry_get(
BER_BVZERO( &anlist[ 1 ].an_name );
}
bsi.bsi_e = ch_malloc( sizeof( Entry ) );
bsi.bsi_e = entry_alloc();
rc = backsql_init_search( &bsi,
ndn,
LDAP_SCOPE_BASE,
......
......@@ -4403,7 +4403,7 @@ Entry *
config_build_entry( Operation *op, SlapReply *rs, CfEntryInfo *parent,
ConfigArgs *c, struct berval *rdn, ConfigOCs *main, ConfigOCs *extra )
{
Entry *e = ch_calloc( 1, sizeof(Entry) );
Entry *e = entry_alloc();
CfEntryInfo *ce = ch_calloc( 1, sizeof(CfEntryInfo) );
struct berval val;
struct berval ad_name;
......
......@@ -128,7 +128,7 @@ slap_create_context_csn_entry(
struct berval bv;
e = (Entry *) ch_calloc( 1, sizeof( Entry ));
e = entry_alloc();
attr_merge( e, slap_schema.si_ad_objectClass,
ocbva, NULL );
......
......@@ -47,18 +47,53 @@ const Entry slap_entry_root = {
NOID, { 0, "" }, { 0, "" }, NULL, 0, { 0, "" }, NULL
};
/*
* these mutexes must be used when calling the entry2str()
* routine since it returns a pointer to static data.
*/
ldap_pvt_thread_mutex_t entry2str_mutex;
static const struct berval dn_bv = BER_BVC("dn");
/*
* Entry free list
*
* Allocate in chunks, minimum of 1000 at a time.
*/
#define CHUNK_SIZE 1000
typedef struct slap_list {
struct slap_list *next;
} slap_list;
static slap_list *entry_chunks;
static Entry *entry_list;
static ldap_pvt_thread_mutex_t entry_mutex;
int entry_destroy(void)
{
slap_list *e;
if ( ebuf ) free( ebuf );
ebuf = NULL;
ecur = NULL;
emaxsize = 0;
return 0;
for ( e=entry_chunks; e; e=entry_chunks ) {
entry_chunks = e->next;
free( e );
}
ldap_pvt_thread_mutex_destroy( &entry_mutex );
ldap_pvt_thread_mutex_destroy( &entry2str_mutex );
return attr_destroy();
}
int entry_init(void)
{
ldap_pvt_thread_mutex_init( &entry2str_mutex );
ldap_pvt_thread_mutex_init( &entry_mutex );
return attr_init();
}
Entry *
str2entry( char *s )
{
......@@ -97,8 +132,7 @@ str2entry2( char *s, int checkvals )
Debug( LDAP_DEBUG_TRACE, "=> str2entry: \"%s\"\n",
s ? s : "NULL", 0, 0 );
/* initialize reader/writer lock */
e = (Entry *) ch_calloc( 1, sizeof(Entry) );
e = entry_alloc();
if( e == NULL ) {
Debug( LDAP_DEBUG_ANY,
......@@ -228,7 +262,7 @@ str2entry2( char *s, int checkvals )
if (( ad_prev && ad != ad_prev ) || ( i == lines )) {
int j, k;
atail->a_next = (Attribute *) ch_malloc( sizeof(Attribute) );
atail->a_next = attr_alloc( NULL );
atail = atail->a_next;
atail->a_flags = 0;
atail->a_desc = ad_prev;
......@@ -409,26 +443,27 @@ entry_clean( Entry *e )
/* e_private must be freed by the caller */
assert( e->e_private == NULL );
e->e_private = NULL;
/* free DNs */
if ( !BER_BVISNULL( &e->e_name ) ) {
free( e->e_name.bv_val );
BER_BVZERO( &e->e_name );
}
if ( !BER_BVISNULL( &e->e_nname ) ) {
free( e->e_nname.bv_val );
BER_BVZERO( &e->e_nname );
}
if ( !BER_BVISNULL( &e->e_bv ) ) {
free( e->e_bv.bv_val );
BER_BVZERO( &e->e_bv );
}
if ( &e->e_abv ) {
free( e->e_abv );
}
/* free attributes */
attrs_free( e->e_attrs );
e->e_attrs = NULL;
memset(e, 0, sizeof(Entry));
}
void
......@@ -436,9 +471,52 @@ entry_free( Entry *e )
{
entry_clean( e );
free( e );
ldap_pvt_thread_mutex_lock( &entry_mutex );
e->e_private = entry_list;
entry_list = e;
ldap_pvt_thread_mutex_unlock( &entry_mutex );
}
int
entry_prealloc( int num )
{
Entry *e;
slap_list *s;
if (!num) return 0;
s = ch_calloc( 1, sizeof(slap_list) + num * sizeof(Entry));
s->next = entry_chunks;
entry_chunks = s;
e = (Entry *)(s+1);
for ( ;num>1; num--) {
e->e_private = e+1;
e++;
}
e->e_private = entry_list;
entry_list = (Entry *)(s+1);
return 0;
}
Entry *
entry_alloc( void )
{
Entry *e;
ldap_pvt_thread_mutex_lock( &entry_mutex );
if ( !entry_list )
entry_prealloc( CHUNK_SIZE );
e = entry_list;
entry_list = e->e_private;
e->e_private = NULL;
ldap_pvt_thread_mutex_unlock( &entry_mutex );
return e;
}
/*
* These routines are used only by Backend.
*
......@@ -616,8 +694,8 @@ int entry_encode(Entry *e, struct berval *bv)
*ptr++ = '\0';
if (a->a_vals) {
for (i=0; a->a_vals[i].bv_val; i++);
entry_putlen(&ptr, i);
for (i=0; a->a_vals[i].bv_val; i++) {
entry_putlen(&ptr, i);
for (i=0; a->a_vals[i].bv_val; i++) {
entry_putlen(&ptr, a->a_vals[i].bv_len);
AC_MEMCPY(ptr, a->a_vals[i].bv_val,
a->a_vals[i].bv_len);
......@@ -679,19 +757,9 @@ int entry_decode(struct berval *bv, Entry **e)
"entry_decode: value count was zero\n", 0, 0, 0);
return LDAP_OTHER;
}
i = sizeof(Entry) + (nattrs * sizeof(Attribute)) +
(nvals * sizeof(struct berval));
#ifdef SLAP_ZONE_ALLOC
x = slap_zn_calloc(1, i + bv->bv_len, ctx);
AC_MEMCPY((char*)x + i, bv->bv_val, bv->bv_len);
bv->bv_val = (char*)x + i;
ptr = (unsigned char *)bv->bv_val;
/* pointer is reset, now advance past nattrs and nvals again */
entry_getlen(&ptr);
entry_getlen(&ptr);
#else
x = ch_calloc(1, i);
#endif
x = entry_alloc();
x->e_attrs = attrs_alloc( nattrs );
x->e_abv = ch_malloc( nvals * sizeof( struct berval ));
i = entry_getlen(&ptr);
x->e_name.bv_val = (char *) ptr;
x->e_name.bv_len = i;
......@@ -705,21 +773,13 @@ int entry_decode(struct berval *bv, Entry **e)
x->e_dn, 0, 0 );
x->e_bv = *bv;
/* A valid entry must have at least one attr, so this
* pointer can never be NULL
*/
x->e_attrs = (Attribute *)(x+1);
bptr = (BerVarray)x->e_attrs;
a = NULL;
a = x->e_attrs;
bptr = x->e_abv;