Main Page | Class Hierarchy | Data Structures | Directories | File List | Data Fields | Related Pages

mp_region.c

00001 /*-
00002  * See the file LICENSE for redistribution information.
00003  *
00004  * Copyright (c) 1996-2005
00005  *      Sleepycat Software.  All rights reserved.
00006  *
00007  * $Id: mp_region.c,v 12.7 2005/08/08 14:30:03 bostic Exp $
00008  */
00009 
00010 #include "db_config.h"
00011 
00012 #ifndef NO_SYSTEM_INCLUDES
00013 #include <sys/types.h>
00014 
00015 #include <string.h>
00016 #endif
00017 
00018 #include "db_int.h"
00019 #include "dbinc/db_shash.h"
00020 #include "dbinc/mp.h"
00021 
00022 static int      __memp_init __P((DB_ENV *, DB_MPOOL *, u_int, u_int32_t));
00023 static int      __memp_init_config __P((DB_ENV *, MPOOL *));
00024 static void     __memp_region_size __P((DB_ENV *, roff_t *, u_int32_t *));
00025 
00026 /*
00027  * __memp_open --
00028  *      Internal version of memp_open: only called from DB_ENV->open.
00029  *
00030  * PUBLIC: int __memp_open __P((DB_ENV *));
00031  */
00032 int
00033 __memp_open(dbenv)
00034         DB_ENV *dbenv;
00035 {
00036         DB_MPOOL *dbmp;
00037         MPOOL *mp;
00038         REGINFO reginfo;
00039         roff_t reg_size;
00040         u_int i;
00041         u_int32_t htab_buckets, *regids;
00042         int ret;
00043 
00044         /* Calculate the region size and hash bucket count. */
00045         __memp_region_size(dbenv, &reg_size, &htab_buckets);
00046 
00047         /* Create and initialize the DB_MPOOL structure. */
00048         if ((ret = __os_calloc(dbenv, 1, sizeof(*dbmp), &dbmp)) != 0)
00049                 return (ret);
00050         LIST_INIT(&dbmp->dbregq);
00051         TAILQ_INIT(&dbmp->dbmfq);
00052         dbmp->dbenv = dbenv;
00053 
00054         /* Join/create the first mpool region. */
00055         memset(&reginfo, 0, sizeof(REGINFO));
00056         reginfo.dbenv = dbenv;
00057         reginfo.type = REGION_TYPE_MPOOL;
00058         reginfo.id = INVALID_REGION_ID;
00059         reginfo.flags = REGION_JOIN_OK;
00060         if (F_ISSET(dbenv, DB_ENV_CREATE))
00061                 F_SET(&reginfo, REGION_CREATE_OK);
00062         if ((ret = __db_r_attach(dbenv, &reginfo, reg_size)) != 0)
00063                 goto err;
00064 
00065         /*
00066          * If we created the region, initialize it.  Create or join any
00067          * additional regions.
00068          */
00069         if (F_ISSET(&reginfo, REGION_CREATE)) {
00070                 /*
00071                  * We define how many regions there are going to be, allocate
00072                  * the REGINFO structures and create them.  Make sure we don't
00073                  * clear the wrong entries on error.
00074                  */
00075                 dbmp->nreg = dbenv->mp_ncache;
00076                 if ((ret = __os_calloc(dbenv,
00077                     dbmp->nreg, sizeof(REGINFO), &dbmp->reginfo)) != 0)
00078                         goto err;
00079                 /* Make sure we don't clear the wrong entries on error. */
00080                 for (i = 0; i < dbmp->nreg; ++i)
00081                         dbmp->reginfo[i].id = INVALID_REGION_ID;
00082                 dbmp->reginfo[0] = reginfo;
00083 
00084                 /* Initialize the first region. */
00085                 if ((ret = __memp_init(dbenv, dbmp, 0, htab_buckets)) != 0)
00086                         goto err;
00087 
00088                 /*
00089                  * Create/initialize remaining regions and copy their IDs into
00090                  * the first region.
00091                  */
00092                 mp = R_ADDR(dbmp->reginfo, dbmp->reginfo[0].rp->primary);
00093                 regids = R_ADDR(dbmp->reginfo, mp->regids);
00094                 for (i = 1; i < dbmp->nreg; ++i) {
00095                         dbmp->reginfo[i].dbenv = dbenv;
00096                         dbmp->reginfo[i].type = REGION_TYPE_MPOOL;
00097                         dbmp->reginfo[i].id = INVALID_REGION_ID;
00098                         dbmp->reginfo[i].flags = REGION_CREATE_OK;
00099                         if ((ret = __db_r_attach(
00100                             dbenv, &dbmp->reginfo[i], reg_size)) != 0)
00101                                 goto err;
00102                         if ((ret =
00103                             __memp_init(dbenv, dbmp, i, htab_buckets)) != 0)
00104                                 goto err;
00105 
00106                         regids[i] = dbmp->reginfo[i].id;
00107                 }
00108         } else {
00109                 /*
00110                  * Determine how many regions there are going to be, allocate
00111                  * the REGINFO structures and fill in local copies of that
00112                  * information.
00113                  */
00114                 mp = R_ADDR(&reginfo, reginfo.rp->primary);
00115                 dbmp->nreg = mp->nreg;
00116                 if ((ret = __os_calloc(dbenv,
00117                     dbmp->nreg, sizeof(REGINFO), &dbmp->reginfo)) != 0)
00118                         goto err;
00119                 /* Make sure we don't clear the wrong entries on error. */
00120                 for (i = 0; i < dbmp->nreg; ++i)
00121                         dbmp->reginfo[i].id = INVALID_REGION_ID;
00122                 dbmp->reginfo[0] = reginfo;
00123 
00124                 /* Join remaining regions. */
00125                 regids = R_ADDR(dbmp->reginfo, mp->regids);
00126                 for (i = 1; i < dbmp->nreg; ++i) {
00127                         dbmp->reginfo[i].dbenv = dbenv;
00128                         dbmp->reginfo[i].type = REGION_TYPE_MPOOL;
00129                         dbmp->reginfo[i].id = regids[i];
00130                         dbmp->reginfo[i].flags = REGION_JOIN_OK;
00131                         if ((ret = __db_r_attach(
00132                             dbenv, &dbmp->reginfo[i], 0)) != 0)
00133                                 goto err;
00134                 }
00135         }
00136 
00137         /* Set the local addresses for the regions. */
00138         for (i = 0; i < dbmp->nreg; ++i)
00139                 dbmp->reginfo[i].primary =
00140                     R_ADDR(&dbmp->reginfo[i], dbmp->reginfo[i].rp->primary);
00141 
00142         /* If the region is threaded, allocate a mutex to lock the handles. */
00143         if ((ret = __mutex_alloc(
00144             dbenv, MTX_MPOOL_HANDLE, DB_MUTEX_THREAD, &dbmp->mutex)) != 0)
00145                 goto err;
00146 
00147         dbenv->mp_handle = dbmp;
00148 
00149         /* A process joining the region may reset the mpool configuration. */
00150         if ((ret = __memp_init_config(dbenv, mp)) != 0)
00151                 return (ret);
00152 
00153         return (0);
00154 
00155 err:    dbenv->mp_handle = NULL;
00156         if (dbmp->reginfo != NULL && dbmp->reginfo[0].addr != NULL) {
00157                 for (i = 0; i < dbmp->nreg; ++i)
00158                         if (dbmp->reginfo[i].id != INVALID_REGION_ID)
00159                                 (void)__db_r_detach(
00160                                     dbenv, &dbmp->reginfo[i], 0);
00161                 __os_free(dbenv, dbmp->reginfo);
00162         }
00163 
00164         (void)__mutex_free(dbenv, &dbmp->mutex);
00165         __os_free(dbenv, dbmp);
00166         return (ret);
00167 }
00168 
00169 /*
00170  * __memp_init --
00171  *      Initialize a MPOOL structure in shared memory.
00172  */
00173 static int
00174 __memp_init(dbenv, dbmp, reginfo_off, htab_buckets)
00175         DB_ENV *dbenv;
00176         DB_MPOOL *dbmp;
00177         u_int reginfo_off;
00178         u_int32_t htab_buckets;
00179 {
00180         DB_MPOOL_HASH *htab;
00181         MPOOL *mp;
00182         REGINFO *reginfo;
00183         u_int32_t i;
00184         int ret;
00185         void *p;
00186 
00187         reginfo = &dbmp->reginfo[reginfo_off];
00188         if ((ret = __db_shalloc(
00189             reginfo, sizeof(MPOOL), 0, &reginfo->primary)) != 0)
00190                 goto mem_err;
00191         reginfo->rp->primary = R_OFFSET(reginfo, reginfo->primary);
00192         mp = reginfo->primary;
00193         memset(mp, 0, sizeof(*mp));
00194 
00195         if ((ret =
00196             __mutex_alloc(dbenv, MTX_MPOOL_REGION, 0, &mp->mtx_region)) != 0)
00197                 return (ret);
00198 
00199         if (reginfo_off == 0) {
00200                 SH_TAILQ_INIT(&mp->mpfq);
00201 
00202                 ZERO_LSN(mp->lsn);
00203 
00204                 mp->nreg = dbmp->nreg;
00205                 if ((ret = __db_shalloc(&dbmp->reginfo[0],
00206                     dbmp->nreg * sizeof(u_int32_t), 0, &p)) != 0)
00207                         goto mem_err;
00208                 mp->regids = R_OFFSET(dbmp->reginfo, p);
00209         }
00210 
00211         /* Allocate hash table space and initialize it. */
00212         if ((ret = __db_shalloc(reginfo,
00213             htab_buckets * sizeof(DB_MPOOL_HASH), 0, &htab)) != 0)
00214                 goto mem_err;
00215         mp->htab = R_OFFSET(reginfo, htab);
00216         for (i = 0; i < htab_buckets; i++) {
00217                 if ((ret = __mutex_alloc(
00218                     dbenv, MTX_MPOOL_HASH_BUCKET, 0, &htab[i].mtx_hash)) != 0)
00219                         return (ret);
00220                 SH_TAILQ_INIT(&htab[i].hash_bucket);
00221                 htab[i].hash_page_dirty = htab[i].hash_priority = 0;
00222         }
00223         mp->htab_buckets = mp->stat.st_hash_buckets = htab_buckets;
00224 
00225         /*
00226          * Only the environment creator knows the total cache size, fill in
00227          * those statistics now.
00228          */
00229         mp->stat.st_gbytes = dbenv->mp_gbytes;
00230         mp->stat.st_bytes = dbenv->mp_bytes;
00231         return (0);
00232 
00233 mem_err:__db_err(dbenv, "Unable to allocate memory for mpool region");
00234         return (ret);
00235 }
00236 
00237 /*
00238  * __memp_region_size --
00239  *      Size the region and figure out how many hash buckets we'll have.
00240  */
00241 static void
00242 __memp_region_size(dbenv, reg_sizep, htab_bucketsp)
00243         DB_ENV *dbenv;
00244         roff_t *reg_sizep;
00245         u_int32_t *htab_bucketsp;
00246 {
00247         roff_t reg_size;
00248 
00249         /* Figure out how big each cache region is. */
00250         reg_size = (roff_t)(dbenv->mp_gbytes / dbenv->mp_ncache) * GIGABYTE;
00251         reg_size += ((roff_t)(dbenv->mp_gbytes %
00252             dbenv->mp_ncache) * GIGABYTE) / dbenv->mp_ncache;
00253         reg_size += dbenv->mp_bytes / dbenv->mp_ncache;
00254         *reg_sizep = reg_size;
00255 
00256         /*
00257          * Figure out how many hash buckets each region will have.  Assume we
00258          * want to keep the hash chains with under 10 pages on each chain.  We
00259          * don't know the pagesize in advance, and it may differ for different
00260          * files.  Use a pagesize of 1K for the calculation -- we walk these
00261          * chains a lot, they must be kept short.
00262          *
00263          * XXX
00264          * Cache sizes larger than 10TB would cause 32-bit wrapping in the
00265          * calculation of the number of hash buckets.  This probably isn't
00266          * something we need to worry about right now, but is checked when the
00267          * cache size is set.
00268          */
00269         *htab_bucketsp = __db_tablesize((u_int32_t)(reg_size / (10 * 1024)));
00270 }
00271 
00272 /*
00273  * __memp_region_mutex_count --
00274  *      Return the number of mutexes the mpool region will need.
00275  *
00276  * PUBLIC: u_int32_t __memp_region_mutex_count __P((DB_ENV *));
00277  */
00278 u_int32_t
00279 __memp_region_mutex_count(dbenv)
00280         DB_ENV *dbenv;
00281 {
00282         roff_t reg_size;
00283         u_int32_t htab_buckets;
00284 
00285         __memp_region_size(dbenv, &reg_size, &htab_buckets);
00286 
00287         /*
00288          * We need a couple of mutexes for the region itself, and one for each
00289          * file handle (MPOOLFILE).  More importantly, each configured cache
00290          * has one mutex per hash bucket and buffer header.  Hash buckets are
00291          * configured to have 10 pages or fewer on each chain, but we don't
00292          * want to fail if we have a large number of 512 byte pages, so double
00293          * the guess.
00294          */
00295         return (dbenv->mp_ncache * htab_buckets * 21 + 50);
00296 }
00297 
00298 /*
00299  * __memp_init_config --
00300  *      Initialize shared configuration information.
00301  */
00302 static int
00303 __memp_init_config(dbenv, mp)
00304         DB_ENV *dbenv;
00305         MPOOL *mp;
00306 {
00307         MPOOL_SYSTEM_LOCK(dbenv);
00308 
00309         if (dbenv->mp_mmapsize != 0)
00310                 mp->mp_mmapsize = dbenv->mp_mmapsize;
00311         if (dbenv->mp_maxopenfd != 0)
00312                 mp->mp_maxopenfd = dbenv->mp_maxopenfd;
00313         if (dbenv->mp_maxwrite != 0)
00314                 mp->mp_maxwrite = dbenv->mp_maxwrite;
00315         if (dbenv->mp_maxwrite_sleep != 0)
00316                 mp->mp_maxwrite_sleep = dbenv->mp_maxwrite_sleep;
00317 
00318         MPOOL_SYSTEM_UNLOCK(dbenv);
00319 
00320         return (0);
00321 }
00322 
00323 /*
00324  * __memp_dbenv_refresh --
00325  *      Clean up after the mpool system on a close or failed open.
00326  *
00327  * PUBLIC: int __memp_dbenv_refresh __P((DB_ENV *));
00328  */
00329 int
00330 __memp_dbenv_refresh(dbenv)
00331         DB_ENV *dbenv;
00332 {
00333         BH *bhp;
00334         DB_MPOOL *dbmp;
00335         DB_MPOOLFILE *dbmfp;
00336         DB_MPOOL_HASH *hp;
00337         DB_MPREG *mpreg;
00338         MPOOL *mp;
00339         REGINFO *reginfo;
00340         u_int32_t bucket, i;
00341         int ret, t_ret;
00342 
00343         ret = 0;
00344         dbmp = dbenv->mp_handle;
00345 
00346         /*
00347          * If a private region, return the memory to the heap.  Not needed for
00348          * filesystem-backed or system shared memory regions, that memory isn't
00349          * owned by any particular process.
00350          *
00351          * Discard buffers.
00352          */
00353         if (F_ISSET(dbenv, DB_ENV_PRIVATE))
00354                 for (i = 0; i < dbmp->nreg; ++i) {
00355                         reginfo = &dbmp->reginfo[i];
00356                         mp = reginfo->primary;
00357                         for (hp = R_ADDR(reginfo, mp->htab), bucket = 0;
00358                             bucket < mp->htab_buckets; ++hp, ++bucket)
00359                                 while ((bhp = SH_TAILQ_FIRST(
00360                                     &hp->hash_bucket, __bh)) != NULL)
00361                                         if ((t_ret = __memp_bhfree(
00362                                             dbmp, hp, bhp,
00363                                             BH_FREE_FREEMEM |
00364                                             BH_FREE_UNLOCKED)) != 0 && ret == 0)
00365                                                 ret = t_ret;
00366                 }
00367 
00368         /* Discard DB_MPOOLFILEs. */
00369         while ((dbmfp = TAILQ_FIRST(&dbmp->dbmfq)) != NULL)
00370                 if ((t_ret = __memp_fclose(dbmfp, 0)) != 0 && ret == 0)
00371                         ret = t_ret;
00372 
00373         /* Discard DB_MPREGs. */
00374         if (dbmp->pg_inout != NULL)
00375                 __os_free(dbenv, dbmp->pg_inout);
00376         while ((mpreg = LIST_FIRST(&dbmp->dbregq)) != NULL) {
00377                 LIST_REMOVE(mpreg, q);
00378                 __os_free(dbenv, mpreg);
00379         }
00380 
00381         /* Discard the DB_MPOOL thread mutex. */
00382         if ((t_ret = __mutex_free(dbenv, &dbmp->mutex)) != 0 && ret == 0)
00383                 ret = t_ret;
00384 
00385         if (F_ISSET(dbenv, DB_ENV_PRIVATE)) {
00386                 /* Discard REGION IDs. */
00387                 reginfo = &dbmp->reginfo[0];
00388                 mp = dbmp->reginfo[0].primary;
00389                 __db_shalloc_free(reginfo, R_ADDR(reginfo, mp->regids));
00390 
00391                 /* Discard Hash tables. */
00392                 for (i = 0; i < dbmp->nreg; ++i) {
00393                         reginfo = &dbmp->reginfo[i];
00394                         mp = reginfo->primary;
00395                         __db_shalloc_free(reginfo, R_ADDR(reginfo, mp->htab));
00396                 }
00397         }
00398 
00399         /* Detach from the region. */
00400         for (i = 0; i < dbmp->nreg; ++i) {
00401                 reginfo = &dbmp->reginfo[i];
00402                 if ((t_ret = __db_r_detach(dbenv, reginfo, 0)) != 0 && ret == 0)
00403                         ret = t_ret;
00404         }
00405 
00406         /* Discard DB_MPOOL. */
00407         __os_free(dbenv, dbmp->reginfo);
00408         __os_free(dbenv, dbmp);
00409 
00410         dbenv->mp_handle = NULL;
00411         return (ret);
00412 }

Generated on Sun Dec 25 12:14:41 2005 for Berkeley DB 4.4.16 by  doxygen 1.4.2