2 * See the file LICENSE for redistribution information.
4 * Copyright (c) 1996, 1997
5 * Sleepycat Software. All rights reserved.
10 static const char sccsid[] = "@(#)mp_fopen.c 10.30 (Sleepycat) 10/25/97";
13 #ifndef NO_SYSTEM_INCLUDES
14 #include <sys/types.h>
28 #include "common_ext.h"
30 static int __memp_mf_close __P((DB_MPOOL *, DB_MPOOLFILE *));
31 static int __memp_mf_open __P((DB_MPOOL *,
32 DB_MPOOLFILE *, int, size_t, int, DBT *, u_int8_t *, int, MPOOLFILE **));
36 * Open a backing file for the memory pool.
39 memp_fopen(dbmp, path, ftype,
40 flags, mode, pagesize, lsn_offset, pgcookie, fileid, retp)
43 int ftype, flags, mode, lsn_offset;
51 /* Validate arguments. */
52 if ((ret = __db_fchk(dbmp->dbenv,
53 "memp_fopen", flags, DB_CREATE | DB_NOMMAP | DB_RDONLY)) != 0)
56 return (__memp_fopen(dbmp, path, ftype,
57 flags, mode, pagesize, lsn_offset, pgcookie, fileid, 1, retp));
62 * Open a backing file for the memory pool; internal version.
64 * PUBLIC: int __memp_fopen __P((DB_MPOOL *, const char *, int, int,
65 * PUBLIC: int, size_t, int, DBT *, u_int8_t *, int, DB_MPOOLFILE **));
68 __memp_fopen(dbmp, path,
69 ftype, flags, mode, pagesize, lsn_offset, pgcookie, fileid, needlock, retp)
72 int ftype, flags, mode, lsn_offset, needlock;
87 /* Require a non-zero pagesize. */
89 __db_err(dbenv, "memp_fopen: pagesize not specified");
93 /* Allocate and initialize the per-process structure. */
95 (DB_MPOOLFILE *)__db_calloc(1, sizeof(DB_MPOOLFILE))) == NULL) {
96 __db_err(dbenv, "%s: %s",
97 path == NULL ? TEMPORARY : path, strerror(ENOMEM));
102 if (LF_ISSET(DB_RDONLY))
103 F_SET(dbmfp, MP_READONLY);
106 if (LF_ISSET(DB_RDONLY)) {
108 "memp_fopen: temporary files can't be readonly");
112 dbmfp->path = (char *)TEMPORARY;
113 F_SET(dbmfp, MP_PATH_TEMP);
115 /* Calculate the real name for this file. */
116 if ((ret = __db_appname(dbenv,
117 DB_APP_DATA, NULL, path, NULL, &dbmfp->path)) != 0)
119 F_SET(dbmfp, MP_PATH_ALLOC);
123 if ((ret = __db_open(dbmfp->path,
124 LF_ISSET(DB_CREATE | DB_RDONLY), DB_CREATE | DB_RDONLY,
125 mode, &dbmfp->fd)) != 0) {
126 __db_err(dbenv, "%s: %s", dbmfp->path, strerror(ret));
130 /* Don't permit files that aren't a multiple of the pagesize. */
132 __db_ioinfo(dbmfp->path, dbmfp->fd, &size, NULL)) != 0) {
133 __db_err(dbenv, "%s: %s", dbmfp->path, strerror(ret));
136 if (size % pagesize) {
138 "%s: file size not a multiple of the pagesize",
146 * Find/allocate the shared file objects. This includes allocating
147 * space for the per-process thread lock.
151 ret = __memp_mf_open(dbmp, dbmfp, ftype, pagesize,
152 lsn_offset, pgcookie, fileid, F_ISSET(dbmfp, MP_PATH_TEMP), &mfp);
154 F_ISSET(dbmp, MP_LOCKHANDLE) && (ret =
155 __memp_ralloc(dbmp, sizeof(db_mutex_t), NULL, &dbmfp->mutexp)) == 0)
156 LOCKINIT(dbmp, dbmfp->mutexp);
169 * + doesn't require any pgin/pgout support
170 * + the DB_NOMMAP flag wasn't set
171 * + and is less than mp_mmapsize bytes in size
173 * we can mmap it instead of reading/writing buffers. Don't do error
174 * checking based on the mmap call failure. We want to do normal I/O
175 * on the file if the reason we failed was because the file was on an
176 * NFS mounted partition, and we can fail in buffer I/O just as easily
180 * We'd like to test to see if the file is too big to mmap. Since we
181 * don't know what size or type off_t's or size_t's are, or the largest
182 * unsigned integral type is, or what random insanity the local C
183 * compiler will perpetrate, doing the comparison in a portable way is
184 * flatly impossible. Hope that mmap fails if the file is too large.
186 #define DB_MAXMMAPSIZE (10 * 1024 * 1024) /* 10 Mb. */
188 if (!F_ISSET(dbmfp, MP_READONLY))
194 if (LF_ISSET(DB_NOMMAP))
196 if (size > (dbenv == NULL || dbenv->mp_mmapsize == 0 ?
197 DB_MAXMMAPSIZE : (off_t)dbenv->mp_mmapsize))
203 if (__db_map(dbmfp->fd, dbmfp->len, 1, 1, &dbmfp->addr) != 0) {
209 LOCKHANDLE(dbmp, dbmp->mutexp);
210 TAILQ_INSERT_TAIL(&dbmp->dbmfq, dbmfp, q);
211 UNLOCKHANDLE(dbmp, dbmp->mutexp);
217 * Note that we do not have to free the thread mutex, because we
218 * never get to here after we have successfully allocated it.
220 if (F_ISSET(dbmfp, MP_PATH_ALLOC))
223 (void)__db_close(dbmfp->fd);
225 FREE(dbmfp, sizeof(DB_MPOOLFILE));
234 __memp_mf_open(dbmp, dbmfp,
235 ftype, pagesize, lsn_offset, pgcookie, fileid, istemp, retp)
238 int ftype, lsn_offset, istemp;
246 u_int8_t idbuf[DB_FILE_ID_LEN];
249 /* Temporary files can't match previous files. */
254 * Get the file id if we weren't give one. Generated file id's don't
255 * use timestamps, otherwise there'd be no chance of anyone joining
258 if (fileid == NULL) {
260 __db_fileid(dbmp->dbenv, dbmfp->path, 0, idbuf)) != 0)
265 /* Walk the list of MPOOLFILE's, looking for a matching file. */
266 for (mfp = SH_TAILQ_FIRST(&dbmp->mp->mpfq, __mpoolfile);
267 mfp != NULL; mfp = SH_TAILQ_NEXT(mfp, q, __mpoolfile))
269 R_ADDR(dbmp, mfp->fileid_off), DB_FILE_ID_LEN)) {
270 if (ftype != mfp->ftype ||
271 pagesize != mfp->stat.st_pagesize) {
272 __db_err(dbmp->dbenv,
273 "%s: ftype or pagesize changed",
279 /* Found it: increment the reference count. */
284 /* Allocate a new MPOOLFILE. */
285 alloc: if ((ret = __memp_ralloc(dbmp, sizeof(MPOOLFILE), NULL, &mfp)) != 0)
288 /* Initialize the structure. */
289 memset(mfp, 0, sizeof(MPOOLFILE));
293 mfp->lsn_off = lsn_offset;
294 mfp->stat.st_pagesize = pagesize;
296 /* Copy the file path into shared memory. */
297 if ((ret = __memp_ralloc(dbmp,
298 strlen(dbmfp->path) + 1, &mfp->path_off, &p)) != 0)
300 memcpy(p, dbmfp->path, strlen(dbmfp->path) + 1);
302 /* Copy the file identification string into shared memory. */
306 if ((ret = __memp_ralloc(dbmp,
307 DB_FILE_ID_LEN, &mfp->fileid_off, &p)) != 0)
309 memcpy(p, fileid, DB_FILE_ID_LEN);
312 /* Copy the page cookie into shared memory. */
313 if (pgcookie == NULL || pgcookie->size == 0) {
314 mfp->pgcookie_len = 0;
315 mfp->pgcookie_off = 0;
317 if ((ret = __memp_ralloc(dbmp,
318 pgcookie->size, &mfp->pgcookie_off, &p)) != 0)
320 memcpy(p, pgcookie->data, pgcookie->size);
321 mfp->pgcookie_len = pgcookie->size;
324 /* Prepend the MPOOLFILE to the list of MPOOLFILE's. */
325 SH_TAILQ_INSERT_HEAD(&dbmp->mp->mpfq, mfp, q, __mpoolfile);
328 err: if (mfp->path_off != 0)
329 __db_shalloc_free(dbmp->addr,
330 R_ADDR(dbmp, mfp->path_off));
332 __db_shalloc_free(dbmp->addr,
333 R_ADDR(dbmp, mfp->fileid_off));
335 __db_shalloc_free(dbmp->addr, mfp);
345 * Close a backing file for the memory pool.
357 /* Complain if pinned blocks never returned. */
358 if (dbmfp->pinref != 0)
359 __db_err(dbmp->dbenv, "%s: close: %lu blocks left pinned",
360 dbmfp->path, (u_long)dbmfp->pinref);
362 /* Remove the DB_MPOOLFILE structure from the list. */
363 LOCKHANDLE(dbmp, dbmp->mutexp);
364 TAILQ_REMOVE(&dbmp->dbmfq, dbmfp, q);
365 UNLOCKHANDLE(dbmp, dbmp->mutexp);
367 /* Close the underlying MPOOLFILE. */
368 (void)__memp_mf_close(dbmp, dbmfp);
370 /* Discard any mmap information. */
371 if (dbmfp->addr != NULL &&
372 (ret = __db_unmap(dbmfp->addr, dbmfp->len)) != 0)
373 __db_err(dbmp->dbenv, "%s: %s", dbmfp->path, strerror(ret));
375 /* Close the file; temporary files may not yet have been created. */
376 if (dbmfp->fd != -1 && (t_ret = __db_close(dbmfp->fd)) != 0) {
377 __db_err(dbmp->dbenv, "%s: %s", dbmfp->path, strerror(t_ret));
383 if (F_ISSET(dbmfp, MP_PATH_ALLOC))
385 if (dbmfp->mutexp != NULL) {
387 __db_shalloc_free(dbmp->addr, dbmfp->mutexp);
391 /* Discard the DB_MPOOLFILE structure. */
392 FREE(dbmfp, sizeof(DB_MPOOLFILE));
399 * Close down an MPOOLFILE.
402 __memp_mf_close(dbmp, dbmfp)
416 /* If more than a single reference, simply decrement. */
423 * Move any BH's held by the file to the free list. We don't free the
424 * memory itself because we may be discarding the memory pool, and it's
425 * fairly expensive to reintegrate the buffers back into the region for
428 mf_offset = R_OFFSET(dbmp, mfp);
429 for (bhp = SH_TAILQ_FIRST(&mp->bhq, __bh); bhp != NULL; bhp = nbhp) {
430 nbhp = SH_TAILQ_NEXT(bhp, q, __bh);
432 #ifdef DEBUG_NO_DIRTY
433 /* Complain if we find any blocks that were left dirty. */
434 if (F_ISSET(bhp, BH_DIRTY))
435 __db_err(dbmp->dbenv,
436 "%s: close: pgno %lu left dirty; ref %lu",
437 dbmfp->path, (u_long)bhp->pgno, (u_long)bhp->ref);
440 if (bhp->mf_offset == mf_offset) {
441 if (F_ISSET(bhp, BH_DIRTY)) {
442 ++mp->stat.st_page_clean;
443 --mp->stat.st_page_dirty;
445 __memp_bhfree(dbmp, mfp, bhp, 0);
446 SH_TAILQ_INSERT_HEAD(&mp->bhfq, bhp, q, __bh);
450 /* Delete from the list of MPOOLFILEs. */
451 SH_TAILQ_REMOVE(&mp->mpfq, mfp, q, __mpoolfile);
453 /* Free the space. */
454 __db_shalloc_free(dbmp->addr, mfp);
455 __db_shalloc_free(dbmp->addr, R_ADDR(dbmp, mfp->path_off));
456 if (mfp->fileid_off != 0)
457 __db_shalloc_free(dbmp->addr, R_ADDR(dbmp, mfp->fileid_off));
458 if (mfp->pgcookie_off != 0)
459 __db_shalloc_free(dbmp->addr, R_ADDR(dbmp, mfp->pgcookie_off));
461 ret1: UNLOCKREGION(dbmp);