Update from db-2.3.12.
[kopensolaris-gnu/glibc.git] / db2 / mp / mp_sync.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_sync.c     10.15 (Sleepycat) 11/1/97";
11 #endif /* not lint */
12
13 #ifndef NO_SYSTEM_INCLUDES
14 #include <sys/types.h>
15
16 #include <errno.h>
17 #include <stdlib.h>
18 #include <string.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 __bhcmp __P((const void *, const void *));
28
29 /*
30  * memp_sync --
31  *      Mpool sync function.
32  */
33 int
34 memp_sync(dbmp, lsnp)
35         DB_MPOOL *dbmp;
36         DB_LSN *lsnp;
37 {
38         BH *bhp, **bharray;
39         DB_ENV *dbenv;
40         MPOOL *mp;
41         MPOOLFILE *mfp;
42         int ar_cnt, cnt, nalloc, next, notused, ret, wrote;
43
44         dbenv = dbmp->dbenv;
45
46         if (dbenv->lg_info == NULL) {
47                 __db_err(dbenv, "memp_sync: requires logging");
48                 return (EINVAL);
49         }
50
51         /*
52          * We try and write the buffers in page order so that the underlying
53          * filesystem doesn't have to seek and can write contiguous blocks,
54          * plus, we don't want to hold the region lock while we write the
55          * buffers.  Get memory to hold the buffer pointers.  Get a good-size
56          * block, too, because we realloc while holding the region lock if we
57          * run out.
58          */
59         if ((bharray =
60             (BH **)__db_malloc((nalloc = 1024) * sizeof(BH *))) == NULL)
61                 return (ENOMEM);
62
63         LOCKREGION(dbmp);
64
65         /*
66          * If the application is asking about a previous call to memp_sync(),
67          * and we haven't found any buffers that the application holding the
68          * pin couldn't write, return yes or no based on the current count.
69          * Note, if the application is asking about a LSN *smaller* than one
70          * we've already handled or are currently handling, then we return a
71          * result based on the count for the larger LSN.
72          */
73         mp = dbmp->mp;
74         if (!F_ISSET(mp, MP_LSN_RETRY) && log_compare(lsnp, &mp->lsn) <= 0) {
75                 if (mp->lsn_cnt == 0) {
76                         *lsnp = mp->lsn;
77                         ret = 0;
78                 } else
79                         ret = DB_INCOMPLETE;
80                 goto done;
81         }
82
83         /* Else, it's a new checkpoint. */
84         F_CLR(mp, MP_LSN_RETRY);
85
86         /*
87          * Save the LSN.  We know that it's a new LSN or larger than the one
88          * for which we were already doing a checkpoint.  (BTW, I don't expect
89          * to see multiple LSN's from the same or multiple processes, but You
90          * Just Never Know.  Responding as if they all called with the largest
91          * of the LSNs specified makes everything work.)
92          *
93          * We don't currently use the LSN we save.  We could potentially save
94          * the last-written LSN in each buffer header and use it to determine
95          * what buffers need to be written.  The problem with this is that it's
96          * sizeof(LSN) more bytes of buffer header.  We currently write all the
97          * dirty buffers instead.
98          *
99          * Walk the list of shared memory segments clearing the count of
100          * buffers waiting to be written.
101          */
102         mp->lsn = *lsnp;
103         mp->lsn_cnt = 0;
104         for (mfp = SH_TAILQ_FIRST(&dbmp->mp->mpfq, __mpoolfile);
105             mfp != NULL; mfp = SH_TAILQ_NEXT(mfp, q, __mpoolfile))
106                 mfp->lsn_cnt = 0;
107
108         /*
109          * Walk the list of buffers and mark all dirty buffers to be written
110          * and all pinned buffers to be potentially written (we can't know if
111          * we'll need to write them until the holding process returns them to
112          * the cache).  We do this in one pass while holding the region locked
113          * so that processes can't make new buffers dirty, causing us to never
114          * finish.  Since the application may have restarted the sync, clear
115          * any BH_WRITE flags that appear to be left over from previous calls.
116          *
117          * Keep a count of the total number of buffers we need to write in
118          * MPOOL->lsn_cnt, and for each file, in MPOOLFILE->lsn_count.
119          */
120         ar_cnt = 0;
121         for (bhp = SH_TAILQ_FIRST(&mp->bhq, __bh);
122             bhp != NULL; bhp = SH_TAILQ_NEXT(bhp, q, __bh))
123                 if (F_ISSET(bhp, BH_DIRTY) || bhp->ref != 0) {
124                         F_SET(bhp, BH_WRITE);
125
126                         ++mp->lsn_cnt;
127
128                         mfp = R_ADDR(dbmp, bhp->mf_offset);
129                         ++mfp->lsn_cnt;
130
131                         /*
132                          * If the buffer isn't in use, we should be able to
133                          * write it immediately, so save a reference to it.
134                          */
135                         if (bhp->ref == 0) {
136                                 if (ar_cnt == nalloc) {
137                                         nalloc *= 2;
138                                         if ((bharray =
139                                             (BH **)__db_realloc(bharray,
140                                             nalloc * sizeof(BH *))) == NULL) {
141                                                 ret = ENOMEM;
142                                                 goto err;
143                                         }
144                                 }
145                                 bharray[ar_cnt++] = bhp;
146                         }
147                 } else
148                         F_CLR(bhp, BH_WRITE);
149
150         /* If there no buffers we can write immediately, we're done. */
151         if (ar_cnt == 0) {
152                 ret = mp->lsn_cnt ? DB_INCOMPLETE : 0;
153                 goto done;
154         }
155
156         /* Lock down the buffers and their contents. */
157         for (cnt = 0; cnt < ar_cnt; ++cnt)
158                 ++bharray[cnt]->ref;
159
160         UNLOCKREGION(dbmp);
161
162         /* Sort the buffers we're going to write. */
163         qsort(bharray, ar_cnt, sizeof(BH *), __bhcmp);
164
165         LOCKREGION(dbmp);
166
167         /* Walk the array, writing buffers. */
168         for (next = 0; next < ar_cnt; ++next) {
169                 /*
170                  * It's possible for a thread to have gotten the buffer since
171                  * we listed it for writing.  If the reference count is still
172                  * 1, we're the only ones using the buffer, go ahead and write.
173                  * If it's >1, then skip the buffer and assume that it will be
174                  * written when it's returned to the cache.
175                  */
176                 if (bharray[next]->ref > 1) {
177                         --bharray[next]->ref;
178                         continue;
179                 }
180
181                 /* Write the buffer. */
182                 mfp = R_ADDR(dbmp, bharray[next]->mf_offset);
183                 ret =
184                     __memp_bhwrite(dbmp, mfp, bharray[next], &notused, &wrote);
185
186                 /* Release the buffer. */
187                 --bharray[next]->ref;
188
189                 /* If there's an error, release the rest of the buffers. */
190                 if (ret != 0 || !wrote) {
191                         while (++next < ar_cnt)
192                                 --bharray[next]->ref;
193
194                         if (ret != 0)
195                                 goto err;
196
197                         /*
198                          * Any process syncing the shared memory buffer pool
199                          * had better be able to write to any underlying file.
200                          * Be understanding, but firm, on this point.
201                          */
202                         if (!wrote) {
203                                 __db_err(dbenv, "%s: unable to flush page: %lu",
204                                     R_ADDR(dbmp, mfp->path_off),
205                                     (u_long)bharray[next]->pgno);
206                                 ret = EPERM;
207                                 goto err;
208                         }
209                 }
210         }
211         ret = mp->lsn_cnt ? DB_INCOMPLETE : 0;
212
213 done:
214         if (0) {
215 err:            /*
216                  * On error, clear:
217                  *      MPOOL->lsn_cnt (the total sync count)
218                  *      MPOOLFILE->lsn_cnt (the per-file sync count)
219                  *      BH_WRITE flag (the scheduled for writing flag)
220                  */
221                 mp->lsn_cnt = 0;
222                 for (mfp = SH_TAILQ_FIRST(&dbmp->mp->mpfq, __mpoolfile);
223                     mfp != NULL; mfp = SH_TAILQ_NEXT(mfp, q, __mpoolfile))
224                         mfp->lsn_cnt = 0;
225                 for (bhp = SH_TAILQ_FIRST(&mp->bhq, __bh);
226                     bhp != NULL; bhp = SH_TAILQ_NEXT(bhp, q, __bh))
227                         F_CLR(bhp, BH_WRITE);
228         }
229         UNLOCKREGION(dbmp);
230         __db_free(bharray);
231         return (ret);
232 }
233
234 /*
235  * memp_fsync --
236  *      Mpool file sync function.
237  */
238 int
239 memp_fsync(dbmfp)
240         DB_MPOOLFILE *dbmfp;
241 {
242         BH *bhp, **bharray;
243         DB_MPOOL *dbmp;
244         size_t mf_offset;
245         int ar_cnt, cnt, nalloc, next, pincnt, notused, ret, wrote;
246
247         /*
248          * If this handle doesn't have a file descriptor that's open for
249          * writing, or if the file is a temporary, there's no reason to
250          * proceed further.
251          */
252         if (F_ISSET(dbmfp, MP_READONLY | MP_PATH_TEMP))
253                 return (0);
254
255         ret = 0;
256         dbmp = dbmfp->dbmp;
257         mf_offset = R_OFFSET(dbmp, dbmfp->mfp);
258
259         /*
260          * We try and write the buffers in page order so that the underlying
261          * filesystem doesn't have to seek and can write contiguous blocks,
262          * plus, we don't want to hold the region lock while we write the
263          * buffers.  Get memory to hold the buffer pointers.  Get a good-size
264          * block, too, because we realloc while holding the region lock if we
265          * run out.
266          */
267         nalloc = 1024;
268         if ((bharray =
269             (BH **)__db_malloc((size_t)nalloc * sizeof(BH *))) == NULL)
270                 return (ENOMEM);
271
272         LOCKREGION(dbmp);
273
274         /*
275          * Walk the LRU list of buffer headers, and get a list of buffers to
276          * write for this MPOOLFILE.
277          */
278         ar_cnt = pincnt = 0;
279         for (bhp = SH_TAILQ_FIRST(&dbmp->mp->bhq, __bh);
280             bhp != NULL; bhp = SH_TAILQ_NEXT(bhp, q, __bh)) {
281                 if (!F_ISSET(bhp, BH_DIRTY) || bhp->mf_offset != mf_offset)
282                         continue;
283                 if (bhp->ref != 0 || F_ISSET(bhp, BH_LOCKED)) {
284                         ++pincnt;
285                         continue;
286                 }
287
288                 if (ar_cnt == nalloc) {
289                         nalloc *= 2;
290                         if ((bharray = (BH **)__db_realloc(bharray,
291                             nalloc * sizeof(BH *))) == NULL) {
292                                 ret = ENOMEM;
293                                 goto err;
294                         }
295                 }
296
297                 bharray[ar_cnt++] = bhp;
298         }
299
300         /* Lock down the buffers and their contents. */
301         for (cnt = 0; cnt < ar_cnt; ++cnt)
302                 ++bharray[cnt]->ref;
303
304         UNLOCKREGION(dbmp);
305
306         /* Sort the buffers we're going to write. */
307         qsort(bharray, ar_cnt, sizeof(BH *), __bhcmp);
308
309         LOCKREGION(dbmp);
310
311         /* Walk the array, writing buffers. */
312         for (next = 0; next < ar_cnt; ++next) {
313                 /*
314                  * It's possible for a thread to have gotten the buffer since
315                  * we listed it for writing.  If the reference count is still
316                  * 1, we're the only ones using the buffer, go ahead and write.
317                  * If it's >1, then skip the buffer and assume that it will be
318                  * written when it's returned to the cache.
319                  */
320                 if (bharray[next]->ref > 1) {
321                         ++pincnt;
322
323                         --bharray[next]->ref;
324                         continue;
325                 }
326
327                 /* Write the buffer. */
328                 ret = __memp_pgwrite(dbmfp, bharray[next], &notused, &wrote);
329
330                 /* Release the buffer. */
331                 --bharray[next]->ref;
332
333                 /* If there's an error, release the rest of the buffers. */
334                 if (ret != 0) {
335                         while (++next < ar_cnt)
336                                 --bharray[next]->ref;
337                         goto err;
338                 }
339                 if (!wrote)
340                         ++pincnt;
341         }
342
343 err:    UNLOCKREGION(dbmp);
344
345         __db_free(bharray);
346
347         /*
348          * Sync the underlying file as the last thing we do, so that the OS
349          * has maximal opportunity to flush buffers before we request it.
350          *
351          * XXX:
352          * Don't lock the region around the sync, fsync(2) has no atomicity
353          * issues.
354          */
355         if (ret == 0)
356                 return (pincnt == 0 ? __db_fsync(dbmfp->fd) : DB_INCOMPLETE);
357         return (ret);
358
359 }
360
361 /*
362  * memp_trickle --
363  *      Keep a specified percentage of the buffers clean.
364  */
365 int
366 memp_trickle(dbmp, pct, nwrotep)
367         DB_MPOOL *dbmp;
368         int pct, *nwrotep;
369 {
370         BH *bhp;
371         MPOOL *mp;
372         MPOOLFILE *mfp;
373         u_long total;
374         int notused, ret, wrote;
375
376         mp = dbmp->mp;
377         if (nwrotep != NULL)
378                 *nwrotep = 0;
379
380         if (pct < 1 || pct > 100)
381                 return (EINVAL);
382
383         LOCKREGION(dbmp);
384
385         /*
386          * If there are sufficient clean buffers, or no buffers or no dirty
387          * buffers, we're done.
388          *
389          * XXX
390          * Using st_page_clean and st_page_dirty is our only choice at the
391          * moment, but it's not as correct as we might like in the presence
392          * of pools with more than one buffer size, as a free 512-byte buffer
393          * isn't the same as a free 8K buffer.
394          */
395 loop:   total = mp->stat.st_page_clean + mp->stat.st_page_dirty;
396         if (total == 0 || mp->stat.st_page_dirty == 0 ||
397             (mp->stat.st_page_clean * 100) / total >= (u_long)pct) {
398                 UNLOCKREGION(dbmp);
399                 return (0);
400         }
401
402         /* Loop until we write a buffer. */
403         for (bhp = SH_TAILQ_FIRST(&mp->bhq, __bh);
404             bhp != NULL; bhp = SH_TAILQ_NEXT(bhp, q, __bh)) {
405                 if (bhp->ref != 0 ||
406                     !F_ISSET(bhp, BH_DIRTY) || F_ISSET(bhp, BH_LOCKED))
407                         continue;
408
409                 mfp = R_ADDR(dbmp, bhp->mf_offset);
410                 if ((ret =
411                     __memp_bhwrite(dbmp, mfp, bhp, &notused, &wrote)) != 0)
412                         goto err;
413
414                 /*
415                  * Any process syncing the shared memory buffer pool
416                  * had better be able to write to any underlying file.
417                  * Be understanding, but firm, on this point.
418                  */
419                 if (!wrote) {
420                         __db_err(dbmp->dbenv, "%s: unable to flush page: %lu",
421                             R_ADDR(dbmp, mfp->path_off), (u_long)bhp->pgno);
422                         ret = EPERM;
423                         goto err;
424                 }
425
426                 ++mp->stat.st_page_trickle;
427                 if (nwrotep != NULL)
428                         ++*nwrotep;
429                 goto loop;
430         }
431
432         /* No more buffers to write. */
433         return (0);
434
435 err:    UNLOCKREGION(dbmp);
436         return (ret);
437 }
438
439 static int
440 __bhcmp(p1, p2)
441         const void *p1, *p2;
442 {
443         BH *bhp1, *bhp2;
444
445         bhp1 = *(BH **)p1;
446         bhp2 = *(BH **)p2;
447
448         /* Sort by file (shared memory pool offset). */
449         if (bhp1->mf_offset < bhp2->mf_offset)
450                 return (-1);
451         if (bhp1->mf_offset > bhp2->mf_offset)
452                 return (1);
453
454         /* Sort by page in file. */
455         return (bhp1->pgno < bhp2->pgno ? -1 : 1);
456 }