1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 1996, 2013 Oracle and/or its affiliates.  All rights reserved.
5  *
6  * $Id$
7  */
8 
9 #include "db_config.h"
10 
11 #include "db_int.h"
12 #include "dbinc/log.h"
13 #include "dbinc/mp.h"
14 #include "dbinc/txn.h"
15 
16 /*
17  * __memp_dirty --
18  *	Upgrade a page from a read-only to a writable pointer.
19  *
20  * PUBLIC: int __memp_dirty __P((DB_MPOOLFILE *, void *,
21  * PUBLIC:     DB_THREAD_INFO *, DB_TXN *, DB_CACHE_PRIORITY, u_int32_t));
22  */
23 int
__memp_dirty(dbmfp,addrp,ip,txn,priority,flags)24 __memp_dirty(dbmfp, addrp, ip, txn, priority, flags)
25 	DB_MPOOLFILE *dbmfp;
26 	void *addrp;
27 	DB_THREAD_INFO *ip;
28 	DB_TXN *txn;
29 	DB_CACHE_PRIORITY priority;
30 	u_int32_t flags;
31 {
32 	BH *bhp;
33 	DB_MPOOL *dbmp;
34 	DB_MPOOL_HASH *hp;
35 	DB_TXN *ancestor;
36 	ENV *env;
37 	MPOOL *c_mp;
38 #ifdef DIAG_MVCC
39 	MPOOLFILE *mfp;
40 #endif
41 	REGINFO *infop;
42 	int mvcc, ret;
43 	db_pgno_t pgno;
44 	void *pgaddr;
45 
46 	env = dbmfp->env;
47 	dbmp = env->mp_handle;
48 	mvcc = atomic_read(&dbmfp->mfp->multiversion);
49 
50 	/* Convert the page address to a buffer header. */
51 	pgaddr = *(void **)addrp;
52 	bhp = (BH *)((u_int8_t *)pgaddr - SSZA(BH, buf));
53 	pgno = bhp->pgno;
54 
55 	/* If we have it exclusively then its already dirty. */
56 	if (F_ISSET(bhp, BH_EXCLUSIVE)) {
57 		DB_ASSERT(env, F_ISSET(bhp, BH_DIRTY));
58 		return (0);
59 	}
60 
61 	if (flags == 0)
62 		flags = DB_MPOOL_DIRTY;
63 	DB_ASSERT(env, flags == DB_MPOOL_DIRTY || flags == DB_MPOOL_EDIT);
64 
65 	if (F_ISSET(dbmfp, MP_READONLY)) {
66 		__db_errx(env, DB_STR_A("3008",
67 		    "%s: dirty flag set for readonly file page", "%s"),
68 		    __memp_fn(dbmfp));
69 		return (EACCES);
70 	}
71 
72 	for (ancestor = txn;
73 	    ancestor != NULL && ancestor->parent != NULL;
74 	    ancestor = ancestor->parent)
75 		;
76 
77 	if (mvcc && txn != NULL && flags == DB_MPOOL_DIRTY &&
78 	    (!BH_OWNED_BY(env, bhp, ancestor) || SH_CHAIN_HASNEXT(bhp, vc))) {
79 		atomic_inc(env, &bhp->ref);
80 		*(void **)addrp = NULL;
81 		if ((ret = __memp_fput(dbmfp, ip, pgaddr, priority)) != 0) {
82 			__db_errx(env, DB_STR_A("3009",
83 			    "%s: error releasing a read-only page", "%s"),
84 			    __memp_fn(dbmfp));
85 			atomic_dec(env, &bhp->ref);
86 			return (ret);
87 		}
88 		if ((ret = __memp_fget(dbmfp,
89 		    &pgno, ip, txn, flags, addrp)) != 0) {
90 			if (ret != DB_LOCK_DEADLOCK)
91 				__db_errx(env, DB_STR_A("3010",
92 				    "%s: error getting a page for writing",
93 				    "%s"), __memp_fn(dbmfp));
94 			atomic_dec(env, &bhp->ref);
95 			return (ret);
96 		}
97 		atomic_dec(env, &bhp->ref);
98 
99 		/*
100 		 * If the MVCC handle count hasn't changed, we should get a
101 		 * different version of the page.
102 		 */
103 		DB_ASSERT(env, *(void **)addrp != pgaddr ||
104 		    mvcc != atomic_read(&dbmfp->mfp->multiversion));
105 
106 		pgaddr = *(void **)addrp;
107 		bhp = (BH *)((u_int8_t *)pgaddr - SSZA(BH, buf));
108 		DB_ASSERT(env, pgno == bhp->pgno);
109 		return (0);
110 	}
111 
112 	infop = &dbmp->reginfo[bhp->region];
113 	c_mp = infop->primary;
114 	hp = R_ADDR(infop, c_mp->htab);
115 	hp = &hp[bhp->bucket];
116 
117 	/* Drop the shared latch and get an exclusive. We have the buf ref'ed.*/
118 	MUTEX_UNLOCK(env, bhp->mtx_buf);
119 	MUTEX_LOCK(env, bhp->mtx_buf);
120 	DB_ASSERT(env, !F_ISSET(bhp, BH_EXCLUSIVE));
121 	F_SET(bhp, BH_EXCLUSIVE);
122 
123 	/* Set/clear the page bits. */
124 	if (!F_ISSET(bhp, BH_DIRTY)) {
125 #ifdef DIAGNOSTIC
126 		MUTEX_LOCK(env, hp->mtx_hash);
127 #endif
128 		atomic_inc(env, &hp->hash_page_dirty);
129 		F_SET(bhp, BH_DIRTY);
130 #ifdef DIAGNOSTIC
131 		MUTEX_UNLOCK(env, hp->mtx_hash);
132 #endif
133 	}
134 
135 #ifdef DIAG_MVCC
136 	mfp = R_ADDR(env->mp_handle->reginfo, bhp->mf_offset);
137 	MVCC_MPROTECT(bhp->buf, mfp->pagesize, PROT_READ | PROT_WRITE);
138 #endif
139 	DB_ASSERT(env, !F_ISSET(bhp, BH_DIRTY) ||
140 	    atomic_read(&hp->hash_page_dirty) != 0);
141 	return (0);
142 }
143 
144 /*
145  * __memp_shared --
146  *	Downgrade a page from exlusively held to shared.
147  *
148  * PUBLIC: int __memp_shared __P((DB_MPOOLFILE *, void *));
149  */
150 int
__memp_shared(dbmfp,pgaddr)151 __memp_shared(dbmfp, pgaddr)
152 	DB_MPOOLFILE *dbmfp;
153 	void *pgaddr;
154 {
155 	BH *bhp;
156 	ENV *env;
157 
158 	env = dbmfp->env;
159 	/* Convert the page address to a buffer header. */
160 	bhp = (BH *)((u_int8_t *)pgaddr - SSZA(BH, buf));
161 
162 	if (F_ISSET(bhp, BH_DIRTY))
163 		dbmfp->mfp->file_written = 1;
164 	DB_ASSERT(env, F_ISSET(bhp, BH_EXCLUSIVE));
165 	F_CLR(bhp, BH_EXCLUSIVE);
166 	MUTEX_UNLOCK(env, bhp->mtx_buf);
167 	MUTEX_READLOCK(env, bhp->mtx_buf);
168 
169 	return (0);
170 }
171