1 /*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 1996, 1997, 1998 5 * Sleepycat Software. All rights reserved. 6 */ 7 #include "config.h" 8 9 #ifndef lint 10 static const char sccsid[] = "@(#)mp_fput.c 10.24 (Sleepycat) 9/27/98"; 11 #endif /* not lint */ 12 13 #ifndef NO_SYSTEM_INCLUDES 14 #include <sys/types.h> 15 16 #include <errno.h> 17 #endif 18 19 #include "db_int.h" 20 #include "shqueue.h" 21 #include "db_shash.h" 22 #include "mp.h" 23 #include "common_ext.h" 24 25 /* 26 * memp_fput -- 27 * Mpool file put function. 28 */ 29 int 30 memp_fput(dbmfp, pgaddr, flags) 31 DB_MPOOLFILE *dbmfp; 32 void *pgaddr; 33 u_int32_t flags; 34 { 35 BH *bhp; 36 DB_MPOOL *dbmp; 37 MPOOL *mp; 38 int wrote, ret; 39 40 dbmp = dbmfp->dbmp; 41 mp = dbmp->mp; 42 43 MP_PANIC_CHECK(dbmp); 44 45 /* Validate arguments. */ 46 if (flags) { 47 if ((ret = __db_fchk(dbmp->dbenv, "memp_fput", flags, 48 DB_MPOOL_CLEAN | DB_MPOOL_DIRTY | DB_MPOOL_DISCARD)) != 0) 49 return (ret); 50 if ((ret = __db_fcchk(dbmp->dbenv, "memp_fput", 51 flags, DB_MPOOL_CLEAN, DB_MPOOL_DIRTY)) != 0) 52 return (ret); 53 54 if (LF_ISSET(DB_MPOOL_DIRTY) && F_ISSET(dbmfp, MP_READONLY)) { 55 __db_err(dbmp->dbenv, 56 "%s: dirty flag set for readonly file page", 57 __memp_fn(dbmfp)); 58 return (EACCES); 59 } 60 } 61 62 LOCKREGION(dbmp); 63 64 /* Decrement the pinned reference count. */ 65 if (dbmfp->pinref == 0) 66 __db_err(dbmp->dbenv, 67 "%s: put: more blocks returned than retrieved", 68 __memp_fn(dbmfp)); 69 else 70 --dbmfp->pinref; 71 72 /* 73 * If we're mapping the file, there's nothing to do. Because we can 74 * stop mapping the file at any time, we have to check on each buffer 75 * to see if the address we gave the application was part of the map 76 * region. 77 */ 78 if (dbmfp->addr != NULL && pgaddr >= dbmfp->addr && 79 (u_int8_t *)pgaddr <= (u_int8_t *)dbmfp->addr + dbmfp->len) { 80 UNLOCKREGION(dbmp); 81 return (0); 82 } 83 84 /* Convert the page address to a buffer header. */ 85 bhp = (BH *)((u_int8_t *)pgaddr - SSZA(BH, buf)); 86 87 /* Set/clear the page bits. */ 88 if (LF_ISSET(DB_MPOOL_CLEAN) && F_ISSET(bhp, BH_DIRTY)) { 89 ++mp->stat.st_page_clean; 90 --mp->stat.st_page_dirty; 91 F_CLR(bhp, BH_DIRTY); 92 } 93 if (LF_ISSET(DB_MPOOL_DIRTY) && !F_ISSET(bhp, BH_DIRTY)) { 94 --mp->stat.st_page_clean; 95 ++mp->stat.st_page_dirty; 96 F_SET(bhp, BH_DIRTY); 97 } 98 if (LF_ISSET(DB_MPOOL_DISCARD)) 99 F_SET(bhp, BH_DISCARD); 100 101 /* 102 * Check for a reference count going to zero. This can happen if the 103 * application returns a page twice. 104 */ 105 if (bhp->ref == 0) { 106 __db_err(dbmp->dbenv, "%s: page %lu: unpinned page returned", 107 __memp_fn(dbmfp), (u_long)bhp->pgno); 108 UNLOCKREGION(dbmp); 109 return (EINVAL); 110 } 111 112 /* 113 * If more than one reference to the page, we're done. Ignore the 114 * discard flags (for now) and leave it at its position in the LRU 115 * chain. The rest gets done at last reference close. 116 */ 117 if (--bhp->ref > 0) { 118 UNLOCKREGION(dbmp); 119 return (0); 120 } 121 122 /* Move the buffer to the head/tail of the LRU chain. */ 123 SH_TAILQ_REMOVE(&mp->bhq, bhp, q, __bh); 124 if (F_ISSET(bhp, BH_DISCARD)) 125 SH_TAILQ_INSERT_HEAD(&mp->bhq, bhp, q, __bh); 126 else 127 SH_TAILQ_INSERT_TAIL(&mp->bhq, bhp, q); 128 129 /* 130 * If this buffer is scheduled for writing because of a checkpoint, we 131 * need to write it (if we marked it dirty), or update the checkpoint 132 * counters (if we didn't mark it dirty). If we try to write it and 133 * can't, that's not necessarily an error, but set a flag so that the 134 * next time the memp_sync function runs we try writing it there, as 135 * the checkpoint application better be able to write all of the files. 136 */ 137 if (F_ISSET(bhp, BH_WRITE)) 138 if (F_ISSET(bhp, BH_DIRTY)) { 139 if (__memp_bhwrite(dbmp, 140 dbmfp->mfp, bhp, NULL, &wrote) != 0 || !wrote) 141 F_SET(mp, MP_LSN_RETRY); 142 } else { 143 F_CLR(bhp, BH_WRITE); 144 145 --dbmfp->mfp->lsn_cnt; 146 --mp->lsn_cnt; 147 } 148 149 UNLOCKREGION(dbmp); 150 return (0); 151 } 152