]> git.ipfire.org Git - thirdparty/glibc.git/blame - db2/log/log_put.c
Update.
[thirdparty/glibc.git] / db2 / log / log_put.c
CommitLineData
92f1da4d
UD
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
af69217f 10static const char sccsid[] = "@(#)log_put.c 10.24 (Sleepycat) 1/17/98";
92f1da4d
UD
11#endif /* not lint */
12
13#ifndef NO_SYSTEM_INCLUDES
14#include <sys/types.h>
15
16#include <errno.h>
17#include <fcntl.h>
18#include <stdlib.h>
19#include <string.h>
20#include <time.h>
21#include <unistd.h>
22#endif
23
24#include "db_int.h"
25#include "shqueue.h"
26#include "db_page.h"
27#include "log.h"
28#include "hash.h"
29#include "common_ext.h"
30
04be94a8 31static int __log_fill __P((DB_LOG *, DB_LSN *, void *, u_int32_t));
cc3fa755 32static int __log_flush __P((DB_LOG *, const DB_LSN *));
92f1da4d 33static int __log_newfd __P((DB_LOG *));
04be94a8 34static int __log_putr __P((DB_LOG *, DB_LSN *, const DBT *, u_int32_t));
cc3fa755 35static int __log_write __P((DB_LOG *, void *, u_int32_t));
92f1da4d
UD
36
37/*
38 * log_put --
39 * Write a log record.
40 */
41int
42log_put(dblp, lsn, dbt, flags)
43 DB_LOG *dblp;
44 DB_LSN *lsn;
45 const DBT *dbt;
46 int flags;
47{
48 int ret;
49
50 /* Validate arguments. */
51#define OKFLAGS (DB_CHECKPOINT | DB_FLUSH)
52 if (flags != 0) {
53 if ((ret =
54 __db_fchk(dblp->dbenv, "log_put", flags, OKFLAGS)) != 0)
55 return (ret);
56 switch (flags) {
57 case DB_CHECKPOINT:
58 case DB_FLUSH:
59 case 0:
60 break;
61 default:
62 return (__db_ferr(dblp->dbenv, "log_put", 1));
63 }
64 }
65
66 LOCK_LOGREGION(dblp);
92f1da4d 67 ret = __log_put(dblp, lsn, dbt, flags);
92f1da4d 68 UNLOCK_LOGREGION(dblp);
92f1da4d
UD
69 return (ret);
70}
71
72/*
73 * __log_put --
74 * Write a log record; internal version.
75 *
76 * PUBLIC: int __log_put __P((DB_LOG *, DB_LSN *, const DBT *, int));
77 */
78int
79__log_put(dblp, lsn, dbt, flags)
80 DB_LOG *dblp;
81 DB_LSN *lsn;
82 const DBT *dbt;
83 int flags;
84{
af69217f 85 DBT fid_dbt, t;
92f1da4d
UD
86 DB_LSN r_unused;
87 FNAME *fnp;
88 LOG *lp;
89 u_int32_t lastoff;
90 int ret;
91
92 lp = dblp->lp;
93
94 /* If this information won't fit in the file, swap files. */
95 if (lp->lsn.offset + sizeof(HDR) + dbt->size > lp->persist.lg_max) {
96 if (sizeof(HDR) +
97 sizeof(LOGP) + dbt->size > lp->persist.lg_max) {
98 __db_err(dblp->dbenv,
99 "log_put: record larger than maximum file size");
100 return (EINVAL);
101 }
cc3fa755
UD
102
103 /* Flush the log. */
104 if ((ret = __log_flush(dblp, NULL)) != 0)
105 return (ret);
92f1da4d
UD
106
107 /*
108 * Save the last known offset from the previous file, we'll
109 * need it to initialize the persistent header information.
110 */
111 lastoff = lp->lsn.offset;
112
cc3fa755 113 /* Point the current LSN to the new file. */
92f1da4d
UD
114 ++lp->lsn.file;
115 lp->lsn.offset = 0;
cc3fa755
UD
116
117 /* Reset the file write offset. */
92f1da4d
UD
118 lp->w_off = 0;
119 } else
120 lastoff = 0;
121
04be94a8
UD
122 /* Initialize the LSN information returned to the user. */
123 lsn->file = lp->lsn.file;
124 lsn->offset = lp->lsn.offset;
125
92f1da4d
UD
126 /*
127 * Insert persistent information as the first record in every file.
128 * Note that the previous length is wrong for the very first record
129 * of the log, but that's okay, we check for it during retrieval.
130 */
131 if (lp->lsn.offset == 0) {
132 t.data = &lp->persist;
133 t.size = sizeof(LOGP);
04be94a8 134 if ((ret = __log_putr(dblp, lsn,
92f1da4d
UD
135 &t, lastoff == 0 ? 0 : lastoff - lp->len)) != 0)
136 return (ret);
92f1da4d 137
04be94a8
UD
138 /* Update the LSN information returned to the user. */
139 lsn->file = lp->lsn.file;
140 lsn->offset = lp->lsn.offset;
141 }
92f1da4d 142
04be94a8
UD
143 /* Write the application's log record. */
144 if ((ret = __log_putr(dblp, lsn, dbt, lp->lsn.offset - lp->len)) != 0)
92f1da4d
UD
145 return (ret);
146
147 /*
148 * On a checkpoint, we:
149 * Put out the checkpoint record (above).
150 * Save the LSN of the checkpoint in the shared region.
151 * Append the set of file name information into the log.
92f1da4d
UD
152 */
153 if (flags == DB_CHECKPOINT) {
154 lp->c_lsn = *lsn;
155
156 for (fnp = SH_TAILQ_FIRST(&dblp->lp->fq, __fname);
157 fnp != NULL; fnp = SH_TAILQ_NEXT(fnp, q, __fname)) {
af69217f 158 memset(&t, 0, sizeof(t));
cc3fa755 159 t.data = R_ADDR(dblp, fnp->name_off);
92f1da4d
UD
160 t.size = strlen(t.data) + 1;
161 memset(&fid_dbt, 0, sizeof(fid_dbt));
cc3fa755 162 fid_dbt.data = R_ADDR(dblp, fnp->fileid_off);
92f1da4d 163 fid_dbt.size = DB_FILE_ID_LEN;
af69217f
UD
164 if ((ret = __log_register_log(dblp, NULL, &r_unused, 0,
165 LOG_CHECKPOINT, &t, &fid_dbt, fnp->id, fnp->s_type))
166 != 0)
92f1da4d
UD
167 return (ret);
168 }
92f1da4d
UD
169 }
170
cc3fa755
UD
171 /*
172 * On a checkpoint or when flush is requested, we:
173 * Flush the current buffer contents to disk.
174 * Sync the log to disk.
175 */
176 if (flags == DB_FLUSH || flags == DB_CHECKPOINT)
177 if ((ret = __log_flush(dblp, NULL)) != 0)
92f1da4d
UD
178 return (ret);
179
cc3fa755
UD
180 /*
181 * On a checkpoint, we:
182 * Save the time the checkpoint was written.
183 * Reset the bytes written since the last checkpoint.
184 */
185 if (flags == DB_CHECKPOINT) {
186 (void)time(&lp->chkpt);
187 lp->stat.st_wc_bytes = lp->stat.st_wc_mbytes = 0;
92f1da4d 188 }
92f1da4d
UD
189 return (0);
190}
191
192/*
193 * __log_putr --
194 * Actually put a record into the log.
195 */
196static int
04be94a8 197__log_putr(dblp, lsn, dbt, prev)
92f1da4d 198 DB_LOG *dblp;
04be94a8 199 DB_LSN *lsn;
92f1da4d
UD
200 const DBT *dbt;
201 u_int32_t prev;
202{
203 HDR hdr;
204 LOG *lp;
205 int ret;
206
207 lp = dblp->lp;
208
209 /*
210 * Initialize the header. If we just switched files, lsn.offset will
211 * be 0, and what we really want is the offset of the previous record
212 * in the previous file. Fortunately, prev holds the value we want.
213 */
214 hdr.prev = prev;
215 hdr.len = sizeof(HDR) + dbt->size;
216 hdr.cksum = __ham_func4(dbt->data, dbt->size);
217
04be94a8 218 if ((ret = __log_fill(dblp, lsn, &hdr, sizeof(HDR))) != 0)
92f1da4d 219 return (ret);
04be94a8 220 lp->len = sizeof(HDR);
92f1da4d
UD
221 lp->lsn.offset += sizeof(HDR);
222
04be94a8 223 if ((ret = __log_fill(dblp, lsn, dbt->data, dbt->size)) != 0)
92f1da4d 224 return (ret);
04be94a8 225 lp->len += dbt->size;
92f1da4d 226 lp->lsn.offset += dbt->size;
92f1da4d
UD
227 return (0);
228}
229
230/*
231 * log_flush --
232 * Write all records less than or equal to the specified LSN.
233 */
234int
235log_flush(dblp, lsn)
236 DB_LOG *dblp;
237 const DB_LSN *lsn;
cc3fa755
UD
238{
239 int ret;
240
241 LOCK_LOGREGION(dblp);
242 ret = __log_flush(dblp, lsn);
243 UNLOCK_LOGREGION(dblp);
244 return (ret);
245}
246
247/*
248 * __log_flush --
249 * Write all records less than or equal to the specified LSN; internal
250 * version.
251 */
252static int
253__log_flush(dblp, lsn)
254 DB_LOG *dblp;
255 const DB_LSN *lsn;
92f1da4d
UD
256{
257 DB_LSN t_lsn;
258 LOG *lp;
04be94a8 259 int current, ret;
92f1da4d
UD
260
261 ret = 0;
262 lp = dblp->lp;
263
cc3fa755
UD
264 /*
265 * If no LSN specified, flush the entire log by setting the flush LSN
266 * to the last LSN written in the log. Otherwise, check that the LSN
267 * isn't a non-existent record for the log.
268 */
92f1da4d
UD
269 if (lsn == NULL) {
270 t_lsn.file = lp->lsn.file;
271 t_lsn.offset = lp->lsn.offset - lp->len;
272 lsn = &t_lsn;
cc3fa755
UD
273 } else
274 if (lsn->file > lp->lsn.file ||
275 (lsn->file == lp->lsn.file &&
276 lsn->offset > lp->lsn.offset - lp->len)) {
277 __db_err(dblp->dbenv,
278 "log_flush: LSN past current end-of-log");
279 return (EINVAL);
280 }
92f1da4d
UD
281
282 /*
cc3fa755 283 * If the LSN is less than the last-sync'd LSN, we're done. Note,
af69217f 284 * the last-sync LSN saved in s_lsn is the LSN of the first byte
04be94a8 285 * we absolutely know has been written to disk, so the test is <=.
92f1da4d 286 */
cc3fa755 287 if (lsn->file < lp->s_lsn.file ||
04be94a8 288 (lsn->file == lp->s_lsn.file && lsn->offset <= lp->s_lsn.offset))
cc3fa755 289 return (0);
92f1da4d
UD
290
291 /*
292 * We may need to write the current buffer. We have to write the
cc3fa755 293 * current buffer if the flush LSN is greater than or equal to the
04be94a8 294 * buffer's starting LSN.
92f1da4d 295 */
04be94a8 296 current = 0;
cc3fa755 297 if (lp->b_off != 0 &&
04be94a8 298 lsn->file >= lp->f_lsn.file && lsn->offset >= lp->f_lsn.offset) {
92f1da4d 299 if ((ret = __log_write(dblp, lp->buf, lp->b_off)) != 0)
cc3fa755 300 return (ret);
92f1da4d 301
04be94a8
UD
302 lp->b_off = 0;
303 current = 1;
304 }
305
cc3fa755
UD
306 /*
307 * It's possible that this thread may never have written to this log
308 * file. Acquire a file descriptor if we don't already have one.
309 */
310 if (dblp->lfname != dblp->lp->lsn.file)
311 if ((ret = __log_newfd(dblp)) != 0)
312 return (ret);
92f1da4d 313
cc3fa755 314 /* Sync all writes to disk. */
92f1da4d 315 if ((ret = __db_fsync(dblp->lfd)) != 0)
cc3fa755
UD
316 return (ret);
317 ++lp->stat.st_scount;
92f1da4d 318
cc3fa755 319 /*
04be94a8
UD
320 * Set the last-synced LSN, using the LSN of the current buffer. If
321 * the current buffer was flushed, we know the LSN of the first byte
322 * of the buffer is on disk, otherwise, we only know that the LSN of
323 * the record before the one beginning the current buffer is on disk.
cc3fa755 324 */
04be94a8
UD
325 lp->s_lsn = lp->f_lsn;
326 if (!current)
327 --lp->s_lsn.offset;
92f1da4d 328
cc3fa755 329 return (0);
92f1da4d
UD
330}
331
332/*
333 * __log_fill --
334 * Write information into the log.
335 */
336static int
04be94a8 337__log_fill(dblp, lsn, addr, len)
92f1da4d 338 DB_LOG *dblp;
04be94a8 339 DB_LSN *lsn;
92f1da4d
UD
340 void *addr;
341 u_int32_t len;
342{
343 LOG *lp;
344 u_int32_t nrec;
345 size_t nw, remain;
346 int ret;
347
348 /* Copy out the data. */
349 for (lp = dblp->lp; len > 0;) {
04be94a8
UD
350 /*
351 * If we're beginning a new buffer, note the user LSN to which
352 * the first byte of the buffer belongs. We have to know this
353 * when flushing the buffer so that we know if the in-memory
354 * buffer needs to be flushed.
355 */
356 if (lp->b_off == 0)
357 lp->f_lsn = *lsn;
358
92f1da4d
UD
359 /*
360 * If we're on a buffer boundary and the data is big enough,
361 * copy as many records as we can directly from the data.
362 */
363 if (lp->b_off == 0 && len >= sizeof(lp->buf)) {
364 nrec = len / sizeof(lp->buf);
365 if ((ret = __log_write(dblp,
366 addr, nrec * sizeof(lp->buf))) != 0)
367 return (ret);
368 addr = (u_int8_t *)addr + nrec * sizeof(lp->buf);
369 len -= nrec * sizeof(lp->buf);
370 continue;
371 }
372
373 /* Figure out how many bytes we can copy this time. */
374 remain = sizeof(lp->buf) - lp->b_off;
375 nw = remain > len ? len : remain;
376 memcpy(lp->buf + lp->b_off, addr, nw);
377 addr = (u_int8_t *)addr + nw;
378 len -= nw;
379 lp->b_off += nw;
380
381 /* If we fill the buffer, flush it. */
04be94a8
UD
382 if (lp->b_off == sizeof(lp->buf)) {
383 if ((ret =
384 __log_write(dblp, lp->buf, sizeof(lp->buf))) != 0)
385 return (ret);
386 lp->b_off = 0;
387 }
92f1da4d
UD
388 }
389 return (0);
390}
391
392/*
393 * __log_write --
394 * Write the log buffer to disk.
395 */
396static int
397__log_write(dblp, addr, len)
398 DB_LOG *dblp;
399 void *addr;
400 u_int32_t len;
401{
402 LOG *lp;
403 ssize_t nw;
404 int ret;
405
406 /*
407 * If we haven't opened the log file yet or the current one
408 * has changed, acquire a new log file.
409 */
410 lp = dblp->lp;
411 if (dblp->lfd == -1 || dblp->lfname != lp->lsn.file)
412 if ((ret = __log_newfd(dblp)) != 0)
413 return (ret);
414
415 /*
416 * Seek to the offset in the file (someone may have written it
417 * since we last did).
418 */
cc3fa755 419 if ((ret = __db_seek(dblp->lfd, 0, 0, lp->w_off, SEEK_SET)) != 0)
92f1da4d
UD
420 return (ret);
421 if ((ret = __db_write(dblp->lfd, addr, len, &nw)) != 0)
422 return (ret);
423 if (nw != (int32_t)len)
424 return (EIO);
425
04be94a8 426 /* Reset the buffer offset and update the seek offset. */
92f1da4d 427 lp->w_off += len;
cc3fa755
UD
428
429 /* Update written statistics. */
430 if ((lp->stat.st_w_bytes += len) >= MEGABYTE) {
431 lp->stat.st_w_bytes -= MEGABYTE;
432 ++lp->stat.st_w_mbytes;
433 }
434 if ((lp->stat.st_wc_bytes += len) >= MEGABYTE) {
435 lp->stat.st_wc_bytes -= MEGABYTE;
436 ++lp->stat.st_wc_mbytes;
437 }
438 ++lp->stat.st_wcount;
92f1da4d
UD
439
440 return (0);
441}
442
443/*
444 * log_file --
445 * Map a DB_LSN to a file name.
446 */
447int
448log_file(dblp, lsn, namep, len)
449 DB_LOG *dblp;
450 const DB_LSN *lsn;
451 char *namep;
452 size_t len;
453{
454 int ret;
455 char *p;
456
457 LOCK_LOGREGION(dblp);
a5a0310d 458 ret = __log_name(dblp, lsn->file, &p);
92f1da4d 459 UNLOCK_LOGREGION(dblp);
92f1da4d
UD
460 if (ret != 0)
461 return (ret);
462
463 /* Check to make sure there's enough room and copy the name. */
464 if (len < strlen(p)) {
465 *namep = '\0';
466 return (ENOMEM);
467 }
468 (void)strcpy(namep, p);
cc3fa755 469 __db_free(p);
92f1da4d
UD
470
471 return (0);
472}
473
474/*
475 * __log_newfd --
476 * Acquire a file descriptor for the current log file.
477 */
478static int
479__log_newfd(dblp)
480 DB_LOG *dblp;
481{
482 int ret;
483 char *p;
484
485 /* Close any previous file descriptor. */
486 if (dblp->lfd != -1) {
487 (void)__db_close(dblp->lfd);
488 dblp->lfd = -1;
489 }
490
491 /* Get the path of the new file and open it. */
492 dblp->lfname = dblp->lp->lsn.file;
a5a0310d 493 if ((ret = __log_name(dblp, dblp->lfname, &p)) != 0)
92f1da4d 494 return (ret);
cc3fa755 495 if ((ret = __db_open(p,
92f1da4d
UD
496 DB_CREATE | DB_SEQUENTIAL,
497 DB_CREATE | DB_SEQUENTIAL,
498 dblp->lp->persist.mode, &dblp->lfd)) != 0)
499 __db_err(dblp->dbenv,
a5a0310d 500 "log_put: %s: %s", p, strerror(ret));
92f1da4d
UD
501 FREES(p);
502 return (ret);
503}
504
505/*
506 * __log_name --
507 * Return the log name for a particular file.
508 *
a5a0310d 509 * PUBLIC: int __log_name __P((DB_LOG *, int, char **));
92f1da4d
UD
510 */
511int
cc3fa755 512__log_name(dblp, filenumber, namep)
a5a0310d
UD
513 DB_LOG *dblp;
514 char **namep;
cc3fa755 515 int filenumber;
92f1da4d
UD
516{
517 char name[sizeof(LFNAME) + 10];
518
cc3fa755 519 (void)snprintf(name, sizeof(name), LFNAME, filenumber);
a5a0310d
UD
520 return (__db_appname(dblp->dbenv,
521 DB_APP_LOG, dblp->dir, name, NULL, namep));
92f1da4d 522}