2 * See the file LICENSE for redistribution information.
4 * Copyright (c) 1996, 1997, 1998
5 * Sleepycat Software. All rights reserved.
10 static const char sccsid
[] = "@(#)mp_fopen.c 10.47 (Sleepycat) 5/4/98";
13 #ifndef NO_SYSTEM_INCLUDES
14 #include <sys/types.h>
24 #include "common_ext.h"
26 static int __memp_mf_close
__P((DB_MPOOL
*, DB_MPOOLFILE
*));
27 static int __memp_mf_open
__P((DB_MPOOL
*,
28 const char *, size_t, db_pgno_t
, DB_MPOOL_FINFO
*, MPOOLFILE
**));
32 * Open a backing file for the memory pool.
35 memp_fopen(dbmp
, path
, flags
, mode
, pagesize
, finfop
, retp
)
41 DB_MPOOL_FINFO
*finfop
;
46 /* Validate arguments. */
47 if ((ret
= __db_fchk(dbmp
->dbenv
,
48 "memp_fopen", flags
, DB_CREATE
| DB_NOMMAP
| DB_RDONLY
)) != 0)
51 /* Require a non-zero pagesize. */
53 __db_err(dbmp
->dbenv
, "memp_fopen: pagesize not specified");
57 return (__memp_fopen(dbmp
,
58 NULL
, path
, flags
, mode
, pagesize
, 1, finfop
, retp
));
63 * Open a backing file for the memory pool; internal version.
65 * PUBLIC: int __memp_fopen __P((DB_MPOOL *, MPOOLFILE *, const char *,
66 * PUBLIC: u_int32_t, int, size_t, int, DB_MPOOL_FINFO *, DB_MPOOLFILE **));
69 __memp_fopen(dbmp
, mfp
, path
, flags
, mode
, pagesize
, needlock
, finfop
, retp
)
76 DB_MPOOL_FINFO
*finfop
;
84 u_int32_t mbytes
, bytes
;
86 u_int8_t idbuf
[DB_FILE_ID_LEN
];
94 * If mfp is provided, we take the DB_MPOOL_FINFO information from
95 * the mfp. We don't bother initializing everything, because some
96 * of them are expensive to acquire. If no mfp is provided and the
97 * finfop argument is NULL, we default the values.
100 memset(&finfo
, 0, sizeof(finfo
));
102 finfo
.ftype
= mfp
->ftype
;
103 finfo
.pgcookie
= NULL
;
105 finfo
.lsn_offset
= mfp
->lsn_off
;
106 finfo
.clear_len
= mfp
->clear_len
;
109 finfo
.pgcookie
= NULL
;
111 finfo
.lsn_offset
= -1;
117 /* Allocate and initialize the per-process structure. */
119 (DB_MPOOLFILE
*)__db_calloc(1, sizeof(DB_MPOOLFILE
))) == NULL
) {
120 __db_err(dbenv
, "memp_fopen: %s", strerror(ENOMEM
));
125 if (LF_ISSET(DB_RDONLY
))
126 F_SET(dbmfp
, MP_READONLY
);
129 if (LF_ISSET(DB_RDONLY
)) {
131 "memp_fopen: temporary files can't be readonly");
138 /* Get the real name for this file and open it. */
139 if ((ret
= __db_appname(dbenv
,
140 DB_APP_DATA
, NULL
, path
, 0, NULL
, &rpath
)) != 0)
142 if ((ret
= __db_open(rpath
,
143 LF_ISSET(DB_CREATE
| DB_RDONLY
),
144 DB_CREATE
| DB_RDONLY
, mode
, &dbmfp
->fd
)) != 0) {
145 __db_err(dbenv
, "%s: %s", rpath
, strerror(ret
));
149 /* Don't permit files that aren't a multiple of the pagesize. */
150 if ((ret
= __db_ioinfo(rpath
,
151 dbmfp
->fd
, &mbytes
, &bytes
, NULL
)) != 0) {
152 __db_err(dbenv
, "%s: %s", rpath
, strerror(ret
));
155 if (bytes
% pagesize
) {
157 "%s: file size not a multiple of the pagesize",
162 size
= mbytes
* MEGABYTE
+ bytes
;
163 last_pgno
= size
== 0 ? 0 : (size
- 1) / pagesize
;
166 * Get the file id if we weren't given one. Generated file id's
167 * don't use timestamps, otherwise there'd be no chance of any
168 * other process joining the party.
170 if (finfop
->fileid
== NULL
) {
171 if ((ret
= __db_fileid(dbenv
, rpath
, 0, idbuf
)) != 0)
173 finfop
->fileid
= idbuf
;
178 * If we weren't provided an underlying shared object to join with,
179 * find/allocate the shared file objects. Also allocate space for
180 * for the per-process thread lock.
186 ret
= __memp_mf_open(dbmp
,
187 path
, pagesize
, last_pgno
, finfop
, &mfp
);
193 F_ISSET(dbmp
, MP_LOCKHANDLE
) && (ret
=
194 __memp_ralloc(dbmp
, sizeof(db_mutex_t
), NULL
, &dbmfp
->mutexp
)) == 0)
195 LOCKINIT(dbmp
, dbmfp
->mutexp
);
208 * + doesn't require any pgin/pgout support
209 * + the DB_NOMMAP flag wasn't set
210 * + and is less than mp_mmapsize bytes in size
212 * we can mmap it instead of reading/writing buffers. Don't do error
213 * checking based on the mmap call failure. We want to do normal I/O
214 * on the file if the reason we failed was because the file was on an
215 * NFS mounted partition, and we can fail in buffer I/O just as easily
219 * We'd like to test to see if the file is too big to mmap. Since we
220 * don't know what size or type off_t's or size_t's are, or the largest
221 * unsigned integral type is, or what random insanity the local C
222 * compiler will perpetrate, doing the comparison in a portable way is
223 * flatly impossible. Hope that mmap fails if the file is too large.
225 #define DB_MAXMMAPSIZE (10 * 1024 * 1024) /* 10 Mb. */
226 if (F_ISSET(mfp
, MP_CAN_MMAP
)) {
227 if (!F_ISSET(dbmfp
, MP_READONLY
))
228 F_CLR(mfp
, MP_CAN_MMAP
);
230 F_CLR(mfp
, MP_CAN_MMAP
);
231 if (finfop
->ftype
!= 0)
232 F_CLR(mfp
, MP_CAN_MMAP
);
233 if (LF_ISSET(DB_NOMMAP
))
234 F_CLR(mfp
, MP_CAN_MMAP
);
235 if (size
> (dbenv
== NULL
|| dbenv
->mp_mmapsize
== 0 ?
236 DB_MAXMMAPSIZE
: dbenv
->mp_mmapsize
))
237 F_CLR(mfp
, MP_CAN_MMAP
);
240 if (F_ISSET(mfp
, MP_CAN_MMAP
)) {
242 if (__db_mapfile(rpath
,
243 dbmfp
->fd
, dbmfp
->len
, 1, &dbmfp
->addr
) != 0) {
245 F_CLR(mfp
, MP_CAN_MMAP
);
251 LOCKHANDLE(dbmp
, dbmp
->mutexp
);
252 TAILQ_INSERT_TAIL(&dbmp
->dbmfq
, dbmfp
, q
);
253 UNLOCKHANDLE(dbmp
, dbmp
->mutexp
);
259 * Note that we do not have to free the thread mutex, because we
260 * never get to here after we have successfully allocated it.
265 (void)__db_close(dbmfp
->fd
);
267 FREE(dbmfp
, sizeof(DB_MPOOLFILE
));
276 __memp_mf_open(dbmp
, path
, pagesize
, last_pgno
, finfop
, retp
)
281 DB_MPOOL_FINFO
*finfop
;
288 #define ISTEMPORARY (path == NULL)
291 * Walk the list of MPOOLFILE's, looking for a matching file.
292 * Temporary files can't match previous files.
295 for (mfp
= SH_TAILQ_FIRST(&dbmp
->mp
->mpfq
, __mpoolfile
);
296 mfp
!= NULL
; mfp
= SH_TAILQ_NEXT(mfp
, q
, __mpoolfile
)) {
297 if (F_ISSET(mfp
, MP_TEMP
))
299 if (!memcmp(finfop
->fileid
,
300 R_ADDR(dbmp
, mfp
->fileid_off
), DB_FILE_ID_LEN
)) {
301 if (finfop
->clear_len
!= mfp
->clear_len
||
302 finfop
->ftype
!= mfp
->ftype
||
303 pagesize
!= mfp
->stat
.st_pagesize
) {
304 __db_err(dbmp
->dbenv
,
305 "%s: ftype, clear length or pagesize changed",
310 /* Found it: increment the reference count. */
317 /* Allocate a new MPOOLFILE. */
318 if ((ret
= __memp_ralloc(dbmp
, sizeof(MPOOLFILE
), NULL
, &mfp
)) != 0)
322 /* Initialize the structure. */
323 memset(mfp
, 0, sizeof(MPOOLFILE
));
325 mfp
->ftype
= finfop
->ftype
;
326 mfp
->lsn_off
= finfop
->lsn_offset
;
327 mfp
->clear_len
= finfop
->clear_len
;
330 * If the user specifies DB_MPOOL_LAST or DB_MPOOL_NEW on a memp_fget,
331 * we have to know the last page in the file. Figure it out and save
334 mfp
->stat
.st_pagesize
= pagesize
;
335 mfp
->orig_last_pgno
= mfp
->last_pgno
= last_pgno
;
337 F_SET(mfp
, MP_CAN_MMAP
);
341 /* Copy the file path into shared memory. */
342 if ((ret
= __memp_ralloc(dbmp
,
343 strlen(path
) + 1, &mfp
->path_off
, &p
)) != 0)
345 memcpy(p
, path
, strlen(path
) + 1);
347 /* Copy the file identification string into shared memory. */
348 if ((ret
= __memp_ralloc(dbmp
,
349 DB_FILE_ID_LEN
, &mfp
->fileid_off
, &p
)) != 0)
351 memcpy(p
, finfop
->fileid
, DB_FILE_ID_LEN
);
354 /* Copy the page cookie into shared memory. */
355 if (finfop
->pgcookie
== NULL
|| finfop
->pgcookie
->size
== 0) {
356 mfp
->pgcookie_len
= 0;
357 mfp
->pgcookie_off
= 0;
359 if ((ret
= __memp_ralloc(dbmp
,
360 finfop
->pgcookie
->size
, &mfp
->pgcookie_off
, &p
)) != 0)
362 memcpy(p
, finfop
->pgcookie
->data
, finfop
->pgcookie
->size
);
363 mfp
->pgcookie_len
= finfop
->pgcookie
->size
;
366 /* Prepend the MPOOLFILE to the list of MPOOLFILE's. */
367 SH_TAILQ_INSERT_HEAD(&dbmp
->mp
->mpfq
, mfp
, q
, __mpoolfile
);
370 err
: if (mfp
->path_off
!= 0)
371 __db_shalloc_free(dbmp
->addr
,
372 R_ADDR(dbmp
, mfp
->path_off
));
373 if (mfp
->fileid_off
!= 0)
374 __db_shalloc_free(dbmp
->addr
,
375 R_ADDR(dbmp
, mfp
->fileid_off
));
377 __db_shalloc_free(dbmp
->addr
, mfp
);
385 * Close a backing file for the memory pool.
397 /* Complain if pinned blocks never returned. */
398 if (dbmfp
->pinref
!= 0)
399 __db_err(dbmp
->dbenv
, "%s: close: %lu blocks left pinned",
400 __memp_fn(dbmfp
), (u_long
)dbmfp
->pinref
);
402 /* Remove the DB_MPOOLFILE structure from the list. */
403 LOCKHANDLE(dbmp
, dbmp
->mutexp
);
404 TAILQ_REMOVE(&dbmp
->dbmfq
, dbmfp
, q
);
405 UNLOCKHANDLE(dbmp
, dbmp
->mutexp
);
407 /* Close the underlying MPOOLFILE. */
408 (void)__memp_mf_close(dbmp
, dbmfp
);
410 /* Discard any mmap information. */
411 if (dbmfp
->addr
!= NULL
&&
412 (ret
= __db_unmapfile(dbmfp
->addr
, dbmfp
->len
)) != 0)
413 __db_err(dbmp
->dbenv
,
414 "%s: %s", __memp_fn(dbmfp
), strerror(ret
));
416 /* Close the file; temporary files may not yet have been created. */
417 if (dbmfp
->fd
!= -1 && (t_ret
= __db_close(dbmfp
->fd
)) != 0) {
418 __db_err(dbmp
->dbenv
,
419 "%s: %s", __memp_fn(dbmfp
), strerror(t_ret
));
425 if (dbmfp
->mutexp
!= NULL
) {
427 __db_shalloc_free(dbmp
->addr
, dbmfp
->mutexp
);
431 /* Discard the DB_MPOOLFILE structure. */
432 FREE(dbmfp
, sizeof(DB_MPOOLFILE
));
439 * Close down an MPOOLFILE.
442 __memp_mf_close(dbmp
, dbmfp
)
456 /* If more than a single reference, simply decrement. */
463 * Move any BH's held by the file to the free list. We don't free the
464 * memory itself because we may be discarding the memory pool, and it's
465 * fairly expensive to reintegrate the buffers back into the region for
468 mf_offset
= R_OFFSET(dbmp
, mfp
);
469 for (bhp
= SH_TAILQ_FIRST(&mp
->bhq
, __bh
); bhp
!= NULL
; bhp
= nbhp
) {
470 nbhp
= SH_TAILQ_NEXT(bhp
, q
, __bh
);
472 #ifdef DEBUG_NO_DIRTY
473 /* Complain if we find any blocks that were left dirty. */
474 if (F_ISSET(bhp
, BH_DIRTY
))
475 __db_err(dbmp
->dbenv
,
476 "%s: close: pgno %lu left dirty; ref %lu",
478 (u_long
)bhp
->pgno
, (u_long
)bhp
->ref
);
481 if (bhp
->mf_offset
== mf_offset
) {
482 if (F_ISSET(bhp
, BH_DIRTY
)) {
483 ++mp
->stat
.st_page_clean
;
484 --mp
->stat
.st_page_dirty
;
486 __memp_bhfree(dbmp
, mfp
, bhp
, 0);
487 SH_TAILQ_INSERT_HEAD(&mp
->bhfq
, bhp
, q
, __bh
);
491 /* Delete from the list of MPOOLFILEs. */
492 SH_TAILQ_REMOVE(&mp
->mpfq
, mfp
, q
, __mpoolfile
);
494 /* Free the space. */
495 if (mfp
->path_off
!= 0)
496 __db_shalloc_free(dbmp
->addr
, R_ADDR(dbmp
, mfp
->path_off
));
497 if (mfp
->fileid_off
!= 0)
498 __db_shalloc_free(dbmp
->addr
, R_ADDR(dbmp
, mfp
->fileid_off
));
499 if (mfp
->pgcookie_off
!= 0)
500 __db_shalloc_free(dbmp
->addr
, R_ADDR(dbmp
, mfp
->pgcookie_off
));
501 __db_shalloc_free(dbmp
->addr
, mfp
);
503 ret1
: UNLOCKREGION(dbmp
);