/* * PROGRAM: JRD Write Ahead Log Coordinator * MODULE: walc.c * DESCRIPTION: Write Ahead Log low level routines * * The contents of this file are subject to the Interbase Public * License Version 1.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy * of the License at http://www.Inprise.com/IPL.html * * Software distributed under the License is distributed on an * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express * or implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code was created by Inprise Corporation * and its predecessors. Portions created by Inprise Corporation are * Copyright (C) Inprise Corporation. * * All Rights Reserved. * Contributor(s): ______________________________________. * * 2002.10.29 Sean Leyne - Removed obsolete "Netware" port * */ #include "firebird.h" #include #include "../jrd/jrd_time.h" #include "../jrd/jrd.h" #include "../jrd/isc.h" #include "../jrd/file_params.h" #include "../wal/wal.h" #include "../jrd/jrn.h" #include "../jrd/flags.h" #include "../jrd/isc_signal.h" #include "gen/codes.h" #include "../jrd/llio.h" #include "../wal/walc_proto.h" #include "../wal/walf_proto.h" #include "../jrd/gds_proto.h" #include "../jrd/iberr_proto.h" #include "../jrd/isc_proto.h" #include "../jrd/isc_i_proto.h" #include "../jrd/isc_s_proto.h" #include "../jrd/llio_proto.h" #ifdef UNIX #include #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef WIN_NT #include #include #ifdef TEXT #undef TEXT #endif #define TEXT SCHAR #define WAL_MUTEX WAL_handle->wal_mutex #endif #ifdef SUPERSERVER #ifdef WIN_NT #define getpid GetCurrentThreadId #endif #endif #ifndef WAL_MUTEX #define WAL_MUTEX WAL_segment->wals_mutex #endif /* Define a structure to hold WAL configuration variables */ typedef struct walc { TEXT *walc_dbname; USHORT walc_db_page_len; TEXT *walc_logname; SLONG walc_log_partition_offset; SSHORT walc_first_time_log; SLONG walc_new_log_seqno; /* used when first_time_log is TRUE */ SSHORT walc_maxbufs; USHORT walc_bufsize; USHORT walc_blksize; USHORT walc_blks_offset; int walc_segment_length; SLONG walc_max_ckpt_intrvl; LGFILE *walc_log_serial_file_info; SSHORT walc_log_names_count; LGFILE **walc_log_rr_files_info; LGFILE *walc_log_ovflow_file_info; USHORT walc_log_names_buf_len; USHORT walc_log_names_offset; USHORT walc_logf_size; USHORT walc_logf_offset; TEXT *walc_jrn_dirname; USHORT walc_jrn_data_len; UCHAR *walc_jrn_data; SLONG walc_grpc_wait_usecs; struct wal *walc_wal; TEXT *walc_mapfile; } *WALC; static SSHORT check_base_name(STATUS *, TEXT *); static void cleanup(void *); static void init_group_commit_blocks(GRP_COMMIT *); static SSHORT setup_wal_params(STATUS *, TEXT *, USHORT, WALC, SSHORT, UCHAR *); static void wals_initialize(WALC, SH_MEM, int); /* these statics define a round-robin data area for storing textual error messages returned to the user */ static TEXT *wal_failures = NULL, *wal_failures_ptr; #define WAL_FAILURE_SPACE 2048 #define statistics stat #ifdef SHLIB_DEFS #define statistics (*_libgds_stat) extern int statistics(); #endif void WALC_acquire( WAL WAL_handle, WALS * address) { /************************************** * * W A L C _ a c q u i r e * ************************************** * * Functional description * Acquire the WAL segment. If it's busy, wait for it. * **************************************/ WALS WAL_segment; SLONG length; STATUS local_status[ISC_STATUS_LENGTH]; *address = WAL_segment = WAL_handle->wal_segment; ISC_inhibit(); if (ISC_mutex_lock(WAL_MUTEX)) WALC_bug(NULL, WAL_handle->wal_dbname, "WAL mutex lock failed"); if (WAL_handle->wal_shmem_data.sh_mem_length_mapped < WAL_segment->wals_length) { /* Somebody extended the WAL segment since we last accessed it. */ length = WAL_segment->wals_length; WAL_segment = (WALS) ISC_remap_file(local_status, &WAL_handle->wal_shmem_data, length, TRUE); if (WAL_segment == (WALS) NULL) { WALC_release(WAL_handle); WALC_bug(NULL, WAL_handle->wal_dbname, "WAL remap failed"); } WAL_handle->wal_segment = *address = WAL_segment; WAL_handle->wal_length = length; } WAL_segment->wals_last_pid = WAL_handle->wal_pid; WAL_segment->wals_acquire_count++; } void WALC_alarm_handler( EVENT event) { /************************************** * * W A L C _ a l a r m _ h a n d l e r * ************************************** * * Functional description * This handler would just poke the semaphore WAL_GCOMMIT_STALL_SEM. * This is to take care of the situation when an alarm handler * expires before the wait on the samaphore starts. This way, we can * avoid an indefinite wait when there is nobody to poke the * semaphore and the timer also expired becuase of some asynchronous * event (signal) handling. * **************************************/ ISC_event_post(event); } SSHORT WALC_bug( STATUS * status_vector, TEXT * dbname, TEXT * string) { /************************************** * * W A L C _ b u g * ************************************** * * Functional description * Disasterous WAL bug. Issue message and abort process. * **************************************/ STATUS local_status[ISC_STATUS_LENGTH]; IBERR_build_status(local_status, gds_wal_bugcheck, gds_arg_string, dbname, gds_arg_number, (SLONG) getpid(), gds_arg_string, string, 0); gds__log_status(dbname, local_status); gds__print_status(local_status); if (status_vector) { IBERR_build_status(status_vector, gds_bug_check, gds_arg_string, string, 0); return FB_FAILURE; } abort(); } void WALC_build_dbg_filename( TEXT * dbname, TEXT * dbg_filename) { /************************************** * * W A L C _ b u i l d _ d b g _ f i l e n a m e * ************************************** * * Functional description * Generate the debug file name. * **************************************/ strcpy(dbg_filename, dbname); strcat(dbg_filename, ".ww.dbg"); } void WALC_build_logname( TEXT * logname, TEXT * basename, SLONG seqno) { /************************************** * * W A L C _ b u i l d _ l o g n a m e * ************************************** * * Functional description * Generate a log file name. Try to keep the file extension * to three characters. * **************************************/ static TEXT letters[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; TEXT extension[11]; if (seqno < 1000 || seqno > 18575) sprintf(extension, "%d", seqno); else { seqno -= 1000; extension[0] = letters[seqno % 26]; extension[1] = letters[seqno / 26 % 26]; extension[2] = letters[seqno / 26 / 26]; extension[3] = 0; } sprintf(logname, "%s%s.%s", basename, WAL_FNAME_SUBSCRIPT, extension); } SSHORT WALC_check_writer(WAL WAL_handle) { /************************************** * * W A L C _ c h e c k _ w r i t e r * ************************************** * * Functional description * Find WAL writer process (thread). If it is not there, return * FB_FAILURE, else return FB_SUCCESS. * **************************************/ WALS WAL_segment; SLONG writer_pid; /* Look for the WAL writer */ WALC_acquire(WAL_handle, &WAL_segment); writer_pid = WAL_segment->wals_writer_pid; WALC_release(WAL_handle); if (!writer_pid) return FB_FAILURE; return (ISC_check_process_existence(writer_pid, 0, FALSE) ? FB_SUCCESS : FB_FAILURE); } void WALC_fini( STATUS * status_vector, WAL * WAL_handle) { /************************************** * * W A L C _ f i n i * ************************************** * * Functional description * Detach the WAL segment. If this process (thread) did a * WAL shutdown earlier, get rid of the WAL segment and * the corresponding semaphores. * **************************************/ WALS WAL_segment; WAL wal; int flag; TEXT wal_file[MAXPATHLEN]; wal = *WAL_handle; if ((wal == NULL) || ((--wal->wal_count) > 0)) return; wal->wal_count = 0; WAL_segment = wal->wal_segment; if (WAL_segment) { WAL_segment->wals_num_attaches--; flag = (wal->wal_flags & WAL_SHUTDOWN_HANDLE) ? ISC_SEM_REMOVE : 0; #ifdef HAVE_MMAP if (wal->wal_flags & WAL_CLEANUP_HANDLE) flag |= ISC_MEM_REMOVE; #else if (wal->wal_flags & WAL_SHUTDOWN_HANDLE) flag |= ISC_MEM_REMOVE; #endif ISC_unmap_file(status_vector, &wal->wal_shmem_data, flag); if (wal->wal_flags & WAL_CLEANUP_HANDLE) { WAL_BUILD_MAPFNAME(wal_file, wal->wal_dbname); unlink(wal_file); WALC_build_dbg_filename(wal->wal_dbname, wal_file); unlink(wal_file); } } gds__free((SLONG *) * WAL_handle); *WAL_handle = NULL; } SSHORT WALC_init(STATUS * status_vector, WAL * WAL_handle, TEXT * dbname, USHORT db_page_len, TEXT * logname, SLONG log_partition_offset, SSHORT first_time_log, SLONG new_log_seqno, SSHORT wpb_length, UCHAR * wpb, USHORT first_attach) { /************************************** * * W A L C _ i n i t * ************************************** * * Functional description * Initialize Write Ahead Log segment for the database. * Initialize WAL_handle for the process (thread). * * If first_time_log is TRUE then use the new_log_seqno * as the starting sequence number for the set of new log * files. * * Return FB_SUCCESS if initialization succeeds else return FB_FAILURE. * **************************************/ TEXT wal_mapfile[MAXPATHLEN]; struct walc wal_args; SLONG length; WALS WAL_segment; WAL wal; void (*wal_init_routine) (); if (*WAL_handle != NULL) { /* We are already initialized. Just increment the use count. */ (*WAL_handle)->wal_count++; return FB_SUCCESS; } /* Construct a wal filename of the form ".walfile" */ WAL_BUILD_MAPFNAME(wal_mapfile, dbname); wal_args.walc_dbname = dbname; wal_args.walc_db_page_len = db_page_len; wal_args.walc_logname = logname; wal_args.walc_log_partition_offset = log_partition_offset; wal_args.walc_first_time_log = first_time_log; wal_args.walc_new_log_seqno = new_log_seqno; wal_args.walc_mapfile = wal_mapfile; if (first_attach) { /* Now parse WAL paramater block (wpb) */ if (setup_wal_params(status_vector, dbname, db_page_len, &wal_args, wpb_length, wpb) != FB_SUCCESS) { WALC_save_status_strings(status_vector); return FB_FAILURE; } length = wal_args.walc_segment_length; wal_init_routine = reinterpret_cast < void (*) () > (wals_initialize); } else { length = 0; wal_init_routine = NULL; } wal = wal_args.walc_wal = (WAL) gds__alloc(sizeof(struct wal)); /* NOMEM: return error status, FREE: error returns & WAL_fini() */ if (!wal) { status_vector[0] = gds_arg_gds; status_vector[1] = gds_virmemexh; status_vector[2] = gds_arg_end; return FB_FAILURE; } #if (defined UNIX) wal->wal_shmem_data.sh_mem_semaphores = MAX_WALSEMS; #endif if ((WAL_segment = (WALS) ISC_map_file(status_vector, wal_mapfile, (void(*)(void *, sh_mem *, int))wal_init_routine, (void *) &wal_args, length, &wal->wal_shmem_data)) == NULL) { WAL_ERROR_APPEND(status_vector, gds_wal_illegal_attach, dbname); WALC_save_status_strings(status_vector); gds__free((SLONG *) wal); return FB_FAILURE; } if (WAL_segment->wals_version != WALS_VERSION) { WALC_bug(status_vector, dbname, "Inconsistent WAL segment version number"); WALC_save_status_strings(status_vector); gds__free((SLONG *) wal); return FB_FAILURE; } #if (defined WIN_NT) /* For Windows/NT and OS/2, mutex_init needs to be done for every process; for UNIX, it happens once in wals_initialize(). */ if (ISC_mutex_init(wal->wal_mutex, wal_mapfile)) WALC_bug(NULL, dbname, "WAL mutex init failed"); if (!wal_init_routine) { /* If there wasn't an initialize routine then events haven't been initialized. */ EVENT event, shared_event; SSHORT i; event = wal->wal_events + 1; shared_event = WAL_segment->wals_events + 1; for (i = 1; i < MAX_WALSEMS; event++, shared_event++, i++) ISC_event_init_shared(event, WAL_SIGNALS + i, wal_mapfile, shared_event, FALSE); } #endif WAL_segment->wals_num_attaches++; wal->wal_count = 1; wal->wal_pid = getpid(); wal->wal_id = (SLONG) wal->wal_shmem_data.sh_mem_handle; wal->wal_grpc_wait_id = -1; wal->wal_flags = 0; wal->wal_local_info_ptr = NULL; wal->wal_segment = WAL_segment; strcpy(wal->wal_dbname, dbname); *WAL_handle = wal; return FB_SUCCESS; } void WALC_release( WAL WAL_handle) { /************************************** * * W A L C _ r e l e a s e * ************************************** * * Functional description * Release the mapped WAL lock file. * **************************************/ WALS WAL_segment; WAL_segment = WAL_handle->wal_segment; WAL_segment->wals_last_pid = 0; if (ISC_mutex_unlock(WAL_MUTEX)) { WALC_bug(NULL, WAL_handle->wal_dbname, "WAL mutex unlock failed"); } ISC_enable(); } void WALC_save_status_strings( STATUS * vector) { /************************************** * * W A L C _ s a v e _ s t a t u s _ s t r i n g s * ************************************** * * Functional description * Strings in status vectors may be stored in stack variables * or memory pools that are transient. To perserve the information, * copy any included strings to a special buffer. * **************************************/ TEXT *p; STATUS status; USHORT l; /* allocate space for failure strings if it hasn't already been allocated */ if (!wal_failures) { wal_failures_ptr = wal_failures = (TEXT *) gds__alloc((SLONG) WAL_FAILURE_SPACE); /* NOMEM: don't try to copy the status strings, FREE: by cleanup() handler */ if (!wal_failures) return; gds__register_cleanup(cleanup, 0); } while (*vector) { switch (status = *vector++) { case gds_arg_cstring: l = *vector++; case gds_arg_interpreted: case gds_arg_string: p = (TEXT *) * vector; if (status != gds_arg_cstring) l = strlen(p) + 1; /* If there isn't any more room in the buffer, start at the beginning again */ if (wal_failures_ptr + l > wal_failures + WAL_FAILURE_SPACE) wal_failures_ptr = wal_failures; *vector++ = (STATUS) wal_failures_ptr; while (l--) *wal_failures_ptr++ = *p++; break; default: ++vector; break; } } } void WALC_setup_buffer_block( WALS WAL_segment, WALBLK * wblk, SSHORT ckpt) { /************************************** * * W A L C _ s e t u p _ b u f f e r _ b l o c k * ************************************** * * Functional description * Mark the passed buffer as ready to be written. * If 'ckpt' flag is TRUE then this buffer finishes a checkpoint. * Assumes that acquire() has been done, before calling this routine. * ***************************************/ USHORT padding_bytes; /* Add extra bytes for the trailing length word. The block header and trailing block length will be initialized by the WAL writer. Add padding bytes to round-up the block size to the next block boundry, if applicable. */ wblk->walblk_roundup_offset = wblk->walblk_cur_offset + BLK_TAILOVHD; if (WAL_segment->wals_roundup_size) { padding_bytes = WAL_segment->wals_roundup_size - ((int) wblk->walblk_roundup_offset % (int) WAL_segment->wals_roundup_size); wblk->walblk_roundup_offset += (USHORT) ((int) padding_bytes % (int) WAL_segment->wals_roundup_size); } WAL_segment->wals_buf_offset += (wblk->walblk_roundup_offset - wblk->walblk_cur_offset); wblk->walblk_flags |= WALBLK_to_be_written; if (ckpt) wblk->walblk_flags |= WALBLK_checkpoint; } static SSHORT check_base_name( STATUS * status_vector, TEXT * base_name) { /************************************** * * c h e c k _ b a s e _ n a m e * ************************************** * * Functional description * Try to create or open a file with the * given base name. Return FB_SUCCESS or FB_FAILURE. * **************************************/ TEXT dummy_fname[MAXPATHLEN]; SLONG fd; /* First create a dummy log name with the given base name */ WALC_build_logname(dummy_fname, base_name, (SLONG) 1); /* Try to create a new file. Delete it if we're successful. */ if (LLIO_open(status_vector, dummy_fname, LLIO_OPEN_NEW_RW, TRUE, &fd) == FB_SUCCESS) { LLIO_close(status_vector, fd); unlink(dummy_fname); return FB_SUCCESS; } /* Try to open an existing file */ if (LLIO_open (status_vector, dummy_fname, LLIO_OPEN_EXISTING_RW, TRUE, &fd) == FB_SUCCESS) { LLIO_close(status_vector, fd); return FB_SUCCESS; } return FB_FAILURE; } static void cleanup( void *arg) { /************************************** * * c l e a n u p * ************************************** * * Functional description * Exit handler to cleanup dynamically allocated memory. * **************************************/ if (wal_failures) gds__free((SLONG *) wal_failures); wal_failures = NULL; } static void init_group_commit_blocks( GRP_COMMIT * grpc_blks) { /************************************** * * i n i t _ g r o u p _ c o m m i t _ b l o c k s * ************************************** * * Functional description * Initialize the group commit blocks of the WAL segment. * **************************************/ USHORT i; int snum; for (i = 0, snum = WAL_GCOMMIT_WAIT1_SEM; i < MAX_GRP_COMMITTERS; i++, snum++) { grpc_blks[i].grp_commit_coordinator = 0L; grpc_blks[i].grp_commit_size = 0; grpc_blks[i].grp_commit_event_num = snum; } } static SSHORT setup_wal_params( STATUS * status_vector, TEXT * dbname, USHORT db_page_len, WALC wal_args, SSHORT wpb_length, UCHAR * wpb) { /************************************** * * s e t u p _ w a l _ p a r a m s * ************************************** * * Functional description * Parse the WAL parameter block and setup the WAL * attributes like size and number of WAL buffers. * **************************************/ SSHORT buf_count; USHORT buf_size; SLONG ckpt_intrvl; UCHAR *p, *q; int i, j; BOOLEAN done; LGFILE *log_file; TEXT err_buffer[16]; /* First setup the default values */ wal_args->walc_maxbufs = MIN_WALBLKS; wal_args->walc_bufsize = (USHORT) MIN(MAX_WALBUFLEN, 4 * (int) db_page_len); wal_args->walc_max_ckpt_intrvl = DEFAULT_CKPT_INTRVL * OneK; wal_args->walc_log_names_count = 0; wal_args->walc_log_serial_file_info = NULL; wal_args->walc_log_rr_files_info = NULL; wal_args->walc_log_ovflow_file_info = NULL; wal_args->walc_log_names_buf_len = 0; wal_args->walc_jrn_dirname = NULL; wal_args->walc_jrn_data_len = 0; wal_args->walc_grpc_wait_usecs = GRPC_WAIT_USECS; buf_count = 0; buf_size = 0; ckpt_intrvl = 0L; /* Now see if wal parameter block specifies any parameters */ done = FALSE; if ((p = wpb) == NULL) done = TRUE; /* Nothing to parse from the wal paramater block */ q = p + wpb_length; while (!done && (p < q)) { switch (*p++) { case WAL_PARAM_BUF_COUNT: memcpy((SCHAR *) & buf_count, p, sizeof(USHORT)); p += sizeof(USHORT); break; case WAL_PARAM_BUF_LEN: memcpy((SCHAR *) & buf_size, p, sizeof(USHORT)); p += sizeof(USHORT); break; case WAL_PARAM_CKPT_INTRVL: memcpy((SCHAR *) & ckpt_intrvl, p, sizeof(SLONG)); p += sizeof(SLONG); break; case WAL_PARAM_SERIAL_LOG: memcpy((UCHAR *) & wal_args->walc_log_serial_file_info, p, sizeof(UCHAR *)); p += sizeof(UCHAR *); break; case WAL_PARAM_RR_LOGS_COUNT: memcpy((SCHAR *) & wal_args->walc_log_names_count, p, sizeof(USHORT)); p += sizeof(USHORT); break; case WAL_PARAM_RR_LOGS: memcpy((UCHAR *) & wal_args->walc_log_rr_files_info, p, sizeof(UCHAR *)); p += sizeof(UCHAR *); break; case WAL_PARAM_OVFLOW_LOG: memcpy((UCHAR *) & wal_args->walc_log_ovflow_file_info, p, sizeof(UCHAR *)); p += sizeof(UCHAR *); break; case WAL_PARAM_GRPC_WAIT_USECS: memcpy((SCHAR *) & wal_args->walc_grpc_wait_usecs, p, sizeof(wal_args->walc_grpc_wait_usecs)); p += sizeof(wal_args->walc_grpc_wait_usecs); break; case WAL_PARAM_JRN_DIRNAME: wal_args->walc_jrn_dirname = (TEXT *) p; p += (strlen(wal_args->walc_jrn_dirname) + 1); break; case WAL_PARAM_JRN_DATA_LEN: memcpy((SCHAR *) & wal_args->walc_jrn_data_len, p, sizeof(USHORT)); p += sizeof(USHORT); break; case WAL_PARAM_JRN_DATA: memcpy((SCHAR *) & wal_args->walc_jrn_data_len, p, sizeof(USHORT)); p += sizeof(USHORT); wal_args->walc_jrn_data = p; p += wal_args->walc_jrn_data_len; break; case WAL_PARAM_END: done = TRUE; break; default: sprintf(err_buffer, "%d", (int) *(p - 1)); WAL_ERROR(status_vector, gds_wal_invalid_wpb, err_buffer); WALC_save_status_strings(status_vector); return FB_FAILURE; } } /* Now update the default values by the supplied parameter values, if any. */ if (buf_count) wal_args->walc_maxbufs = MIN(MAX_WALBLKS, MAX(MIN_WALBLKS, buf_count)); if (buf_size) /* Should be adjusted for roundup in case of using raw device -- Damodar */ wal_args->walc_bufsize = (USHORT) MIN(MAX_WALBUFLEN, MAX(wal_args->walc_bufsize, buf_size)); if (ckpt_intrvl) wal_args->walc_max_ckpt_intrvl = MAX(ckpt_intrvl * OneK, MIN_CKPT_INTRVL * OneK); /* Process serial log specification. */ if (log_file = wal_args->walc_log_serial_file_info) { /* Make sure that base path name is valid */ if (wal_args->walc_first_time_log && (check_base_name(status_vector, log_file->lg_name) != FB_SUCCESS)) { WALC_save_status_strings(status_vector); return FB_FAILURE; } wal_args->walc_log_names_buf_len += strlen(log_file->lg_name) + 1; /* User specifies log size in units of 1K bytes. */ log_file->lg_size = MAX(log_file->lg_size * OneK, MIN_LOG_LENGTH * OneK); } /* Process overflow log specification. */ if (log_file = wal_args->walc_log_ovflow_file_info) { /* Make sure that base path name is valid */ if (wal_args->walc_first_time_log && (check_base_name(status_vector, log_file->lg_name) != FB_SUCCESS)) { WALC_save_status_strings(status_vector); return FB_FAILURE; } wal_args->walc_log_names_buf_len += strlen(log_file->lg_name) + 1; /* User specifies log size in units of 1K bytes. */ log_file->lg_size = MAX(log_file->lg_size * OneK, MIN_LOG_LENGTH * OneK); } /* Process round-robin logs specifications. */ if (wal_args->walc_log_names_count && wal_args->walc_log_rr_files_info) { /* First make sure that an overflow log file specification is provided */ if (!wal_args->walc_log_ovflow_file_info) { IBERR_build_status(status_vector, gds_wal_ovflow_log_required, 0); return FB_FAILURE; } for (i = 0; i < wal_args->walc_log_names_count; i++) { log_file = wal_args->walc_log_rr_files_info[i]; wal_args->walc_log_names_buf_len += strlen(log_file->lg_name) + 1; /* User specifies log size in units of 1K bytes. */ log_file->lg_size = MAX(log_file->lg_size * OneK, MIN_LOG_LENGTH * OneK); if (wal_args->walc_first_time_log) { /* Pre-allocate a non-raw log file */ if (log_file->lg_flags & LOG_raw) continue; if (WALF_init_p_log(status_vector, dbname, log_file->lg_name, log_file->lg_size, log_file->lg_partitions) != FB_SUCCESS) { WALC_save_status_strings(status_vector); /* Remove the allocated files so far */ for (j = 0; j < i; j++) { log_file = wal_args->walc_log_rr_files_info[j]; if (!(log_file->lg_flags & LOG_raw)) unlink(log_file->lg_name); } return FB_FAILURE; } } } } wal_args->walc_log_names_offset = ROUNDUP_LONG(sizeof(struct wals)); wal_args->walc_logf_size = ROUNDUP_LONG(sizeof(struct logf)); wal_args->walc_logf_offset = wal_args->walc_log_names_offset + ROUNDUP_LONG(wal_args->walc_log_names_buf_len); wal_args->walc_blksize = ROUNDUP_LONG(sizeof(WALBLK) + wal_args->walc_bufsize); wal_args->walc_blks_offset = wal_args->walc_logf_offset + (wal_args->walc_logf_size * wal_args->walc_log_names_count); wal_args->walc_segment_length = wal_args->walc_blks_offset + wal_args->walc_maxbufs * wal_args->walc_blksize; return FB_SUCCESS; } static void wals_initialize( WALC wal_args, SH_MEM shmem_data, int initialize) { /************************************** * * w a l s _ i n i t i a l i z e * ************************************** * * Functional description * Initialize the WAL segment. The caller is assumed * to have an exclusive lock on the WAL file. * **************************************/ WALS WAL_segment; SSHORT maxbufs; SSHORT i; WALBLK *wblk; LOGF *logf; UCHAR *p, *q; LGFILE *log_file; #if (defined WIN_NT) EVENT event, shared_event; #endif #ifdef UNIX struct stat buf; #endif WAL_segment = (WALS) shmem_data->sh_mem_address; #if (defined WIN_NT) event = wal_args->walc_wal->wal_events + 1; shared_event = WAL_segment->wals_events + 1; for (i = 1; i < MAX_WALSEMS; event++, shared_event++, i++) ISC_event_init_shared(event, WAL_SIGNALS + i, wal_args->walc_mapfile, shared_event, (initialize) ? TRUE : FALSE); #endif if (!initialize) return; if (shmem_data->sh_mem_length_mapped < wal_args->walc_segment_length) WALC_bug(NULL, wal_args->walc_dbname, "Insufficient WAL segment length"); WAL_segment->wals_version = WALS_VERSION; WAL_segment->wals_dbpage_size = wal_args->walc_db_page_len; WAL_segment->wals_length = shmem_data->sh_mem_length_mapped; WAL_segment->wals_id = time(NULL); /* Time when this structure was initialized */ #ifdef UNIX if (statistics(wal_args->walc_dbname, &buf) == -1) WALC_bug(NULL, wal_args->walc_dbname, "stat() call failed"); WAL_segment->wals_owner_id = buf.st_uid; WAL_segment->wals_group_id = buf.st_gid; #else WAL_segment->wals_owner_id = 0; WAL_segment->wals_group_id = 0; #endif WAL_segment->wals_writer_pid = 0; #if !(defined WIN_NT) if (ISC_mutex_init(WAL_MUTEX, shmem_data->sh_mem_mutex_arg)) WALC_bug(NULL, wal_args->walc_dbname, "WAL mutex init failed"); #endif WAL_segment->wals_last_pid = getpid(); WAL_segment->wals_num_attaches = 0; WAL_segment->wals_num_detaches = 0; WAL_segment->wals_last_err = 0; WAL_segment->wals_last_bug = 0; strcpy(WAL_segment->wals_dbname, wal_args->walc_dbname); WAL_segment->wals_dbpage_size = wal_args->walc_db_page_len; strcpy(WAL_segment->wals_logname, wal_args->walc_logname); WAL_segment->wals_cur_log_base_offset = 0L; WAL_segment->wals_log_partition_offset = wal_args->walc_log_partition_offset; WAL_segment->wals_max_log_length = DEFAULT_LOG_LENGTH * OneK; WAL_segment->wals_rollover_threshold = WAL_segment->wals_max_log_length; WAL_segment->wals_log_seqno = wal_args->walc_new_log_seqno - 1; /* will be properly used and inititalized by the WAL writer */ /* Now setup the checkpointing info */ WAL_segment->wals_max_ckpt_intrvl = wal_args->walc_max_ckpt_intrvl; WAL_segment->wals_thrshold_intrvl = WAL_segment->wals_max_ckpt_intrvl * 0.8; WAL_segment->wals_cur_ckpt_intrvl = 0L; WAL_segment->wals_ckpted_log_seqno = 0L; WAL_segment->wals_ckpt_log_p_offset = 0L; strcpy(WAL_segment->wals_ckpt_logname, ""); WAL_segment->wals_ckpted_offset = 0L; WAL_segment->wals_saved_ckpted_offset = 0L; WAL_segment->wals_buf_waiters = 0; WAL_segment->wals_buf_waiting_count = 0; WAL_segment->wals_buf_offset = WAL_segment->wals_flushed_offset = 0L; WAL_segment->wals_flushed_log_seqno = 0L; /* Now setup group-commit info */ WAL_segment->wals_grpc_wait_id = 1; WAL_segment->wals_grpc_wait_usecs = wal_args->walc_grpc_wait_usecs; WAL_segment->wals_cur_grpc_blknum = 0; WAL_segment->wals_dummy1 = 0; #if !(defined WIN_NT) for (i = 1; i < MAX_WALSEMS; i++) ISC_event_init(&WAL_segment->wals_events[i], shmem_data->sh_mem_mutex_arg, i); #endif init_group_commit_blocks(WAL_segment->wals_grpc_blks); WAL_segment->wals_grpc_count = 0; WAL_segment->wals_max_logfiles = wal_args->walc_log_names_count; WAL_segment->wals_cur_logfile = -1; q = (UCHAR *) WAL_segment + wal_args->walc_log_names_offset; WAL_segment->wals_logf_offset = wal_args->walc_logf_offset; WAL_segment->wals_logf_size = wal_args->walc_logf_size; /* Now setup the serial log file info */ logf = &WAL_segment->wals_log_serial_file_info; if (log_file = wal_args->walc_log_serial_file_info) { p = (UCHAR *) log_file->lg_name; strcpy(reinterpret_cast < char *>(q), reinterpret_cast < const char *>(p)); logf->logf_name_offset = q - (UCHAR *) WAL_segment; logf->logf_max_size = log_file->lg_size; logf->logf_roundup_size = 0; logf->logf_flags = 0; logf->logf_base_offset = 0L; logf->logf_fname_seqno = 0L; if (log_file->lg_flags & LOG_raw) { logf->logf_roundup_size = 512; logf->logf_flags |= LOGF_RAW; } q += strlen(reinterpret_cast < const char *>(p)) + 1; } else { /* Set some default values */ logf->logf_name_offset = 0; logf->logf_max_size = WAL_segment->wals_max_log_length; logf->logf_roundup_size = 0; logf->logf_flags = 0; logf->logf_base_offset = 0L; logf->logf_fname_seqno = 0L; } /* Now setup the pre-allocated round-robin log files info */ if (wal_args->walc_log_names_count) { for (i = 0; i < wal_args->walc_log_names_count; i++) { log_file = wal_args->walc_log_rr_files_info[i]; p = (UCHAR *) log_file->lg_name; strcpy(reinterpret_cast < char *>(q), reinterpret_cast < const char *>(p)); logf = LOGF_INFO(i); logf->logf_name_offset = q - (UCHAR *) WAL_segment; logf->logf_partitions = log_file->lg_partitions; logf->logf_max_size = log_file->lg_size; logf->logf_roundup_size = 0; logf->logf_flags = 0; logf->logf_base_offset = 0L; logf->logf_fname_seqno = 0L; if (log_file->lg_partitions > 1) { logf->logf_max_size = PARTITION_SIZE(logf->logf_max_size, log_file->lg_partitions); logf->logf_flags |= LOGF_PARTITIONED; } if (log_file->lg_flags & LOG_raw) { logf->logf_roundup_size = 512; logf->logf_flags |= LOGF_RAW; logf->logf_max_size = (logf->logf_max_size & ~((SLONG) 511)); } q += strlen(reinterpret_cast < const char *>(p)) + 1; } } /* Now setup the overflow log file info */ logf = &WAL_segment->wals_log_ovflow_file_info; if (log_file = wal_args->walc_log_ovflow_file_info) { p = (UCHAR *) log_file->lg_name; strcpy(reinterpret_cast < char *>(q), reinterpret_cast < const char *>(p)); logf->logf_name_offset = q - (UCHAR *) WAL_segment; logf->logf_max_size = log_file->lg_size; logf->logf_roundup_size = 0; logf->logf_flags = 0; logf->logf_base_offset = 0L; logf->logf_fname_seqno = 0L; if (log_file->lg_flags & LOG_raw) { logf->logf_roundup_size = 512; logf->logf_flags |= LOGF_RAW; } } else { /* Set some default values */ logf->logf_name_offset = 0; logf->logf_max_size = WAL_segment->wals_max_log_length; logf->logf_roundup_size = 0; logf->logf_flags = 0; logf->logf_base_offset = 0L; logf->logf_fname_seqno = 0L; } /* Now setup the WAL buffers info */ WAL_segment->wals_initial_maxbufs = WAL_segment->wals_maxbufs = maxbufs = wal_args->walc_maxbufs; WAL_segment->wals_curbuf = 0; /* CUR_BUF */ WAL_segment->wals_last_flushed_buf = -1; /* No buffer flushed so far */ WAL_segment->wals_bufsize = wal_args->walc_bufsize; WAL_segment->wals_roundup_size = 0; /* Default - no roundup */ /* Set the maximum allowable size of a log record. It must be fully acccomdable in one WAL buffer. Set here to avoid repeated recalculations later on. */ WAL_segment->wals_max_recsize = wal_args->walc_bufsize - BLK_OVHD - REC_HDROVHD; /* Now setup the long-term journalling info */ if (wal_args->walc_jrn_dirname == NULL) { strcpy(WAL_segment->wals_jrn_dirname, ""); WAL_segment->wals_jrn_init_data_len = 0; } else { strcpy(WAL_segment->wals_jrn_dirname, wal_args->walc_jrn_dirname); WAL_segment->wals_jrn_init_data_len = wal_args->walc_jrn_data_len; memcpy(WAL_segment->wals_jrn_init_data, wal_args->walc_jrn_data, WAL_segment->wals_jrn_init_data_len); } WAL_segment->wals_total_IO_bytes = 0L; WAL_segment->wals_IO_count = 0L; WAL_segment->wals_acquire_count = 0L; WAL_segment->wals_put_count = 0L; WAL_segment->wals_commit_count = 0L; WAL_segment->wals_blksize = wal_args->walc_blksize; WAL_segment->wals_dummy2 = 0; WAL_segment->wals_blkseqno = 1L; WAL_segment->wals_blks_offset = wal_args->walc_blks_offset; WAL_segment->wals_flags = 0L; WAL_segment->wals_flags2 = 0L; if (wal_args->walc_first_time_log) WAL_segment->wals_flags |= WALS_FIRST_TIME_LOG; for (i = 0; i < maxbufs; i++) { wblk = WAL_BLOCK(i); wblk->walblk_number = i; wblk->walblk_flags = 0; wblk->walblk_roundup_offset = 0; wblk->walblk_cur_offset = 0; } return; }