Update from db-2.3.12.
[kopensolaris-gnu/glibc.git] / db2 / mp / mp_bh.c
1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 1996, 1997
5  *      Sleepycat Software.  All rights reserved.
6  */
7 #include "config.h"
8
9 #ifndef lint
10 static const char sccsid[] = "@(#)mp_bh.c       10.21 (Sleepycat) 10/25/97";
11 #endif /* not lint */
12
13 #ifndef NO_SYSTEM_INCLUDES
14 #include <sys/types.h>
15
16 #include <errno.h>
17 #include <string.h>
18 #include <unistd.h>
19 #endif
20
21 #include "db_int.h"
22 #include "shqueue.h"
23 #include "db_shash.h"
24 #include "mp.h"
25 #include "common_ext.h"
26
27 static int __memp_upgrade __P((DB_MPOOL *, DB_MPOOLFILE *, MPOOLFILE *));
28
29 /*
30  * __memp_bhwrite --
31  *      Write the page associated with a given bucket header.
32  *
33  * PUBLIC: int __memp_bhwrite
34  * PUBLIC:     __P((DB_MPOOL *, MPOOLFILE *, BH *, int *, int *));
35  */
36 int
37 __memp_bhwrite(dbmp, mfp, bhp, restartp, wrotep)
38         DB_MPOOL *dbmp;
39         MPOOLFILE *mfp;
40         BH *bhp;
41         int *restartp, *wrotep;
42 {
43         DBT dbt;
44         DB_MPOOLFILE *dbmfp;
45         DB_MPREG *mpreg;
46
47         if (restartp != NULL)
48                 *restartp = 0;
49         if (wrotep != NULL)
50                 *wrotep = 0;
51
52         /*
53          * Walk the process' DB_MPOOLFILE list and find a file descriptor for
54          * the file.  We also check that the descriptor is open for writing.
55          * If we find a descriptor on the file that's not open for writing, we
56          * try and upgrade it to make it writeable.
57          */
58         LOCKHANDLE(dbmp, dbmp->mutexp);
59         for (dbmfp = TAILQ_FIRST(&dbmp->dbmfq);
60             dbmfp != NULL; dbmfp = TAILQ_NEXT(dbmfp, q))
61                 if (dbmfp->mfp == mfp) {
62                         if (F_ISSET(dbmfp, MP_READONLY) &&
63                             __memp_upgrade(dbmp, dbmfp, mfp))
64                                 return (0);
65                         break;
66                 }
67         UNLOCKHANDLE(dbmp, dbmp->mutexp);
68         if (dbmfp != NULL)
69                 goto found;
70
71         /*
72          * It's not a page from a file we've opened.  If the file requires
73          * input/output processing, see if this process has ever registered
74          * information as to how to write this type of file.  If not, there's
75          * nothing we can do.
76          */
77         if (mfp->ftype != 0) {
78                 LOCKHANDLE(dbmp, dbmp->mutexp);
79                 for (mpreg = LIST_FIRST(&dbmp->dbregq);
80                     mpreg != NULL; mpreg = LIST_NEXT(mpreg, q))
81                         if (mpreg->ftype == mfp->ftype)
82                                 break;
83                 UNLOCKHANDLE(dbmp, dbmp->mutexp);
84                 if (mpreg == NULL)
85                         return (0);
86         }
87
88         /*
89          * Try and open the file; ignore any error, assume it's a permissions
90          * problem.
91          *
92          * XXX
93          * There's no negative cache here, so we may repeatedly try and open
94          * files that we have previously tried (and failed) to open.
95          */
96         dbt.size = mfp->pgcookie_len;
97         dbt.data = R_ADDR(dbmp, mfp->pgcookie_off);
98         if (__memp_fopen(dbmp, R_ADDR(dbmp, mfp->path_off),
99             mfp->ftype, 0, 0, mfp->stat.st_pagesize,
100             mfp->lsn_off, &dbt, R_ADDR(dbmp, mfp->fileid_off), 0, &dbmfp) != 0)
101                 return (0);
102
103 found:  return (__memp_pgwrite(dbmfp, bhp, restartp, wrotep));
104 }
105
106 /*
107  * __memp_pgread --
108  *      Read a page from a file.
109  *
110  * PUBLIC: int __memp_pgread __P((DB_MPOOLFILE *, BH *, int));
111  */
112 int
113 __memp_pgread(dbmfp, bhp, can_create)
114         DB_MPOOLFILE *dbmfp;
115         BH *bhp;
116         int can_create;
117 {
118         DB_MPOOL *dbmp;
119         MPOOLFILE *mfp;
120         size_t pagesize;
121         ssize_t nr;
122         int ret;
123
124         dbmp = dbmfp->dbmp;
125         mfp = dbmfp->mfp;
126         pagesize = mfp->stat.st_pagesize;
127
128         F_SET(bhp, BH_LOCKED | BH_TRASH);
129         LOCKBUFFER(dbmp, bhp);
130         UNLOCKREGION(dbmp);
131
132         /*
133          * Temporary files may not yet have been created.
134          *
135          * Seek to the page location.
136          */
137         ret = 0;
138         LOCKHANDLE(dbmp, dbmfp->mutexp);
139         if (dbmfp->fd == -1 || (ret =
140             __db_seek(dbmfp->fd, pagesize, bhp->pgno, 0, SEEK_SET)) != 0) {
141                 if (!can_create) {
142                         if (dbmfp->fd == -1)
143                                 ret = EINVAL;
144                         UNLOCKHANDLE(dbmp, dbmfp->mutexp);
145                         __db_err(dbmp->dbenv,
146                             "%s: page %lu doesn't exist, create flag not set",
147                             dbmfp->path, (u_long)bhp->pgno);
148                         goto err;
149                 }
150                 UNLOCKHANDLE(dbmp, dbmfp->mutexp);
151
152                 /* Clear any uninitialized data. */
153                 memset(bhp->buf, 0, pagesize);
154                 goto pgin;
155         }
156
157         /*
158          * Read the page; short reads are treated like creates, although
159          * any valid data is preserved.
160          */
161         ret = __db_read(dbmfp->fd, bhp->buf, pagesize, &nr);
162         UNLOCKHANDLE(dbmp, dbmfp->mutexp);
163         if (ret != 0)
164                 goto err;
165
166         if (nr == (ssize_t)pagesize)
167                 can_create = 0;
168         else {
169                 if (!can_create) {
170                         ret = EINVAL;
171                         goto err;
172                 }
173
174                 /* Clear any uninitialized data. */
175                 memset(bhp->buf + nr, 0, pagesize - nr);
176         }
177
178         /* Call any pgin function. */
179 pgin:   ret = mfp->ftype == 0 ? 0 : __memp_pg(dbmfp, bhp, 1);
180
181         /* Reacquire the region lock. */
182         LOCKREGION(dbmp);
183
184         /* If the pgin function succeeded, the data is now valid. */
185         if (ret == 0)
186                 F_CLR(bhp, BH_TRASH);
187
188         /* Update the statistics. */
189         if (can_create) {
190                 ++dbmp->mp->stat.st_page_create;
191                 ++mfp->stat.st_page_create;
192         } else {
193                 ++dbmp->mp->stat.st_page_in;
194                 ++mfp->stat.st_page_in;
195         }
196
197         if (0) {
198 err:            LOCKREGION(dbmp);
199         }
200
201         /* Release the buffer. */
202         F_CLR(bhp, BH_LOCKED);
203         UNLOCKBUFFER(dbmp, bhp);
204
205         return (ret);
206 }
207
208 /*
209  * __memp_pgwrite --
210  *      Write a page to a file.
211  *
212  * PUBLIC: int __memp_pgwrite __P((DB_MPOOLFILE *, BH *, int *, int *));
213  */
214 int
215 __memp_pgwrite(dbmfp, bhp, restartp, wrotep)
216         DB_MPOOLFILE *dbmfp;
217         BH *bhp;
218         int *restartp, *wrotep;
219 {
220         DB_ENV *dbenv;
221         DB_LOG *lg_info;
222         DB_LSN lsn;
223         DB_MPOOL *dbmp;
224         MPOOL *mp;
225         MPOOLFILE *mfp;
226         size_t pagesize;
227         ssize_t nw;
228         int callpgin, ret;
229         const char *fail;
230
231         dbmp = dbmfp->dbmp;
232         dbenv = dbmp->dbenv;
233         mp = dbmp->mp;
234         mfp = dbmfp->mfp;
235
236         if (restartp != NULL)
237                 *restartp = 0;
238         if (wrotep != NULL)
239                 *wrotep = 0;
240         callpgin = 0;
241         pagesize = mfp->stat.st_pagesize;
242
243         F_SET(bhp, BH_LOCKED);
244         LOCKBUFFER(dbmp, bhp);
245         UNLOCKREGION(dbmp);
246
247         if (restartp != NULL)
248                 *restartp = 1;
249
250         /* Copy the LSN off the page if we're going to need it. */
251         lg_info = dbenv->lg_info;
252         if (lg_info != NULL || F_ISSET(bhp, BH_WRITE))
253                 memcpy(&lsn, bhp->buf + mfp->lsn_off, sizeof(DB_LSN));
254
255         /* Ensure the appropriate log records are on disk. */
256         if (lg_info != NULL && (ret = log_flush(lg_info, &lsn)) != 0)
257                 goto err;
258
259         /*
260          * Call any pgout function.  We set the callpgin flag so that on
261          * error we flag that the contents of the buffer may be trash.
262          */
263         if (mfp->ftype == 0)
264                 ret = 0;
265         else {
266                 callpgin = 1;
267                 if ((ret = __memp_pg(dbmfp, bhp, 0)) != 0)
268                         goto err;
269         }
270
271         /* Temporary files may not yet have been created. */
272         LOCKHANDLE(dbmp, dbmfp->mutexp);
273         if (dbmfp->fd == -1 && ((ret = __db_appname(dbenv, DB_APP_TMP,
274             NULL, NULL, &dbmfp->fd, NULL)) != 0 || dbmfp->fd == -1)) {
275                 UNLOCKHANDLE(dbmp, dbmfp->mutexp);
276                 __db_err(dbenv, "unable to create temporary backing file");
277                 goto err;
278         }
279
280         /* Write the page out. */
281         if ((ret = __db_seek(dbmfp->fd, pagesize, bhp->pgno, 0, SEEK_SET)) != 0)
282                 fail = "seek";
283         else if ((ret = __db_write(dbmfp->fd, bhp->buf, pagesize, &nw)) != 0)
284                 fail = "write";
285         UNLOCKHANDLE(dbmp, dbmfp->mutexp);
286         if (ret != 0) {
287                 /*
288                  * XXX
289                  * Shut the compiler up; it doesn't understand the correlation
290                  * between the failing clauses to __db_lseek and __db_write and
291                  * this ret != 0.
292                  */
293                 fail = NULL;
294                 goto syserr;
295         }
296
297         if (nw != (ssize_t)pagesize) {
298                 ret = EIO;
299                 fail = "write";
300                 goto syserr;
301         }
302
303         if (wrotep != NULL)
304                 *wrotep = 1;
305
306         /* Reacquire the region lock. */
307         LOCKREGION(dbmp);
308
309         /* Clean up the flags based on a successful write. */
310         F_SET(bhp, BH_CALLPGIN);
311         F_CLR(bhp, BH_DIRTY | BH_LOCKED);
312
313         ++mp->stat.st_page_clean;
314         --mp->stat.st_page_dirty;
315
316         UNLOCKBUFFER(dbmp, bhp);
317
318         /*
319          * If we write a buffer for which a checkpoint is waiting, update
320          * the count of pending buffers (both in the mpool as a whole and
321          * for this file).  If the count for this file goes to zero, flush
322          * the writes.
323          *
324          * XXX:
325          * Don't lock the region around the sync, fsync(2) has no atomicity
326          * issues.
327          *
328          * XXX:
329          * We ignore errors from the sync -- it makes no sense to return an
330          * error to the calling process, so set a flag causing the sync to
331          * be retried later.
332          *
333          * If the buffer we wrote has a LSN larger than the current largest
334          * we've written for this checkpoint, update the saved value.
335          */
336         if (F_ISSET(bhp, BH_WRITE)) {
337                 if (log_compare(&lsn, &mp->lsn) > 0)
338                         mp->lsn = lsn;
339                 F_CLR(bhp, BH_WRITE);
340
341                 --mp->lsn_cnt;
342
343                 if (--mfp->lsn_cnt == 0 && __db_fsync(dbmfp->fd) != 0)
344                         F_SET(mp, MP_LSN_RETRY);
345         }
346
347         /* Update I/O statistics. */
348         ++mp->stat.st_page_out;
349         ++mfp->stat.st_page_out;
350
351         return (0);
352
353 syserr: __db_err(dbenv,
354             "%s: %s failed for page %lu", dbmfp->path, fail, (u_long)bhp->pgno);
355
356 err:    UNLOCKBUFFER(dbmp, bhp);
357         LOCKREGION(dbmp);
358         if (callpgin)
359                 F_SET(bhp, BH_CALLPGIN);
360         F_CLR(bhp, BH_LOCKED);
361         return (ret);
362 }
363
364 /*
365  * __memp_pg --
366  *      Call the pgin/pgout routine.
367  *
368  * PUBLIC: int __memp_pg __P((DB_MPOOLFILE *, BH *, int));
369  */
370 int
371 __memp_pg(dbmfp, bhp, is_pgin)
372         DB_MPOOLFILE *dbmfp;
373         BH *bhp;
374         int is_pgin;
375 {
376         DBT dbt, *dbtp;
377         DB_MPOOL *dbmp;
378         DB_MPREG *mpreg;
379         MPOOLFILE *mfp;
380         int ftype, ret;
381
382         dbmp = dbmfp->dbmp;
383         mfp = dbmfp->mfp;
384
385         LOCKHANDLE(dbmp, dbmp->mutexp);
386
387         ftype = mfp->ftype;
388         for (mpreg = LIST_FIRST(&dbmp->dbregq);
389             mpreg != NULL; mpreg = LIST_NEXT(mpreg, q)) {
390                 if (ftype != mpreg->ftype)
391                         continue;
392                 if (mfp->pgcookie_len == 0)
393                         dbtp = NULL;
394                 else {
395                         dbt.size = mfp->pgcookie_len;
396                         dbt.data = R_ADDR(dbmp, mfp->pgcookie_off);
397                         dbtp = &dbt;
398                 }
399                 UNLOCKHANDLE(dbmp, dbmp->mutexp);
400
401                 if (is_pgin) {
402                         if (mpreg->pgin != NULL && (ret =
403                             mpreg->pgin(bhp->pgno, bhp->buf, dbtp)) != 0)
404                                 goto err;
405                 } else
406                         if (mpreg->pgout != NULL && (ret =
407                             mpreg->pgout(bhp->pgno, bhp->buf, dbtp)) != 0)
408                                 goto err;
409                 break;
410         }
411
412         if (mpreg == NULL)
413                 UNLOCKHANDLE(dbmp, dbmp->mutexp);
414
415         return (0);
416
417 err:    UNLOCKHANDLE(dbmp, dbmp->mutexp);
418         __db_err(dbmp->dbenv, "%s: %s failed for page %lu",
419             dbmfp->path, is_pgin ? "pgin" : "pgout", (u_long)bhp->pgno);
420         return (ret);
421 }
422
423 /*
424  * __memp_bhfree --
425  *      Free a bucket header and its referenced data.
426  *
427  * PUBLIC: void __memp_bhfree __P((DB_MPOOL *, MPOOLFILE *, BH *, int));
428  */
429 void
430 __memp_bhfree(dbmp, mfp, bhp, free_mem)
431         DB_MPOOL *dbmp;
432         MPOOLFILE *mfp;
433         BH *bhp;
434         int free_mem;
435 {
436         size_t off;
437
438         /* Delete the buffer header from the hash bucket queue. */
439         off = BUCKET(dbmp->mp, R_OFFSET(dbmp, mfp), bhp->pgno);
440         SH_TAILQ_REMOVE(&dbmp->htab[off], bhp, hq, __bh);
441
442         /* Delete the buffer header from the LRU queue. */
443         SH_TAILQ_REMOVE(&dbmp->mp->bhq, bhp, q, __bh);
444
445         /*
446          * If we're not reusing it immediately, free the buffer header
447          * and data for real.
448          */
449         if (free_mem) {
450                 __db_shalloc_free(dbmp->addr, bhp);
451                 --dbmp->mp->stat.st_page_clean;
452         }
453 }
454
455 /*
456  * __memp_upgrade --
457  *      Upgrade a file descriptor from readonly to readwrite.
458  */
459 static int
460 __memp_upgrade(dbmp, dbmfp, mfp)
461         DB_MPOOL *dbmp;
462         DB_MPOOLFILE *dbmfp;
463         MPOOLFILE *mfp;
464 {
465         int fd;
466
467         /*
468          * !!!
469          * We expect the handle to already be locked.
470          */
471
472         /* Check to see if we've already upgraded. */
473         if (F_ISSET(dbmfp, MP_UPGRADE))
474                 return (0);
475
476         /* Check to see if we've already failed. */
477         if (F_ISSET(dbmfp, MP_UPGRADE_FAIL))
478                 return (1);
479
480         /* Try the open. */
481         if (__db_open(R_ADDR(dbmp, mfp->path_off), 0, 0, 0, &fd) != 0) {
482                 F_SET(dbmfp, MP_UPGRADE_FAIL);
483                 return (1);
484         }
485
486         /* Swap the descriptors and set the upgrade flag. */
487         (void)__db_close(dbmfp->fd);
488         dbmfp->fd = fd;
489         F_SET(dbmfp, MP_UPGRADE);
490
491         return (0);
492 }