Codebase list fis-gtm / HEAD sr_unix / anticipatory_freeze.c
HEAD

Tree @HEAD (Download .tar.gz)

anticipatory_freeze.c @HEADraw · history · blame

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
/****************************************************************
 *								*
 * Copyright (c) 2012-2021 Fidelity National Information	*
 * Services, Inc. and/or its subsidiaries. All rights reserved.	*
 *								*
 *	This source code contains the intellectual property	*
 *	of its copyright holder(s), and is made available	*
 *	under a license.  If you do not know the terms of	*
 *	the license, please stop and do not read further.	*
 *								*
 ****************************************************************/

#include "mdef.h"

#include <errno.h>
#include "gtm_stdio.h"
#include "gtm_ctype.h"

#include "gdsroot.h"
#include "gdsbt.h"
#include "gdsfhead.h"
#include "filestruct.h"
#include "repl_msg.h"
#include "gtmsource.h"
#include "error.h"
#include "repl_sem.h"
#include "gtmimagename.h"
#include "hashtab_str.h"
#include "eintr_wrappers.h"
#include "gtmmsg.h"
#include "anticipatory_freeze.h"
#ifdef DEBUG
#include "dpgbldir.h"
#include "is_proc_alive.h"
#endif

#define MAX_TAG_LEN				128	/* Maximum size of an error mnemonic */
#define MAX_READ_SZ				1024	/* Mnemonic + flags shouldn't exceed this limit */
#define COMMENT_DELIMITER			';'
#define NEWLINE					0x0A
#define EOL_REACHED				(char *)(-1)
#define EOF_REACHED				(char *)(-2)

#define EXHAUST_CURRENT_LINE(BUFF, HANDLE, FGETS_RC)				\
{										\
	assert(NEWLINE != BUFF[STRLEN(BUFF) - 1]);				\
	while (TRUE)								\
	{									\
		FGETS_FILE(BUFF, MAX_READ_SZ, HANDLE, FGETS_RC);		\
		if ((NULL == FGETS_RC) || NEWLINE == BUFF[STRLEN(BUFF) - 1])	\
			break;							\
	}									\
}

error_def(ERR_ASSERT);
error_def(ERR_CUSTERRNOTFND);
error_def(ERR_CUSTERRSYNTAX);
error_def(ERR_CUSTOMFILOPERR);
error_def(ERR_DSKSPCAVAILABLE);
error_def(ERR_ENOSPCQIODEFER);
error_def(ERR_REPLINSTFREEZECOMMENT);
error_def(ERR_REPLINSTFROZEN);
error_def(ERR_TEXT);
error_def(ERR_INSTFRZDEFER);

GBLREF	jnlpool_addrs_ptr_t	jnlpool;
GBLREF	boolean_t		is_src_server;
GBLREF	boolean_t		holds_sem[NUM_SEM_SETS][NUM_SRC_SEMS];
#ifdef DEBUG
GBLREF	uint4			process_id;
GBLREF	volatile boolean_t	timer_in_handler;
GBLREF	boolean_t		multi_thread_in_use;
#endif

/* Typically prototypes are included in the header file. But, in this case the static function - get_mnemonic_offset - has the
 * hash_table_str as one of the function parameters which means all the files which includes anticipatory_freeze.h needs to include
 * hashtab_str.h and since there are lot of such C files, we chose to define static function prototypes in the C file itself.
 */
STATICFNDCL char	*scan_space(FILE *handle, char *buff, char *buffptr, char *buff_top);
STATICFNDCL int		get_mnemonic_offset(hash_table_str **err_hashtab, char *mnemonic_buf, int mnemonic_len);
STATICFNDCL boolean_t	is_message_excluded(int msg_id);

/* Scan through whitespace in the current buffer (read more if required) */
STATICFNDEF char 	*scan_space(FILE *handle, char *buff, char *buffptr, char *buff_top)
{
	char		*fgets_rc;

	while (TRUE)
	{
		for (; (buffptr < buff_top) && (ISSPACE_ASCII(*buffptr)); buffptr++)
			;
		if (buffptr < buff_top)
			return buffptr;	/* first non-whitespace character */
		if (NEWLINE == *(buffptr - 1))
			return EOL_REACHED;
		/* current buffer is exhausted and we haven't seen a newline; read more */
		FGETS_FILE(buff, MAX_READ_SZ, handle, fgets_rc);
		if (NULL == fgets_rc)
			break;
		buffptr = buff;
		buff_top = buffptr + STRLEN(buff);
	}
	return EOF_REACHED;
}

STATICFNDEF int		get_mnemonic_offset(hash_table_str **err_hashtab, char *mnemonic_buf, int mnemonic_len)
{
	const err_msg			*msg_beg, *msg_top;
	hash_table_str			*tmp_err_hashtab;
	ht_ent_str			*err_htent;
	stringkey			key;
	err_msg				*msg_info;
	boolean_t			added;
	DEBUG_ONLY(int			idx;)

	msg_beg = merrors_ctl.fst_msg;
	msg_top = msg_beg + merrors_ctl.msg_cnt;
	assert((MAX_TAG_LEN > mnemonic_len) && (0 < mnemonic_len)); /* For SCI */
	assert('\0' == mnemonic_buf[mnemonic_len]);
	if (NULL == (tmp_err_hashtab = *err_hashtab))
	{	/* create and populate hash-table for future lookups */
		tmp_err_hashtab = (hash_table_str *)malloc(SIZEOF(hash_table_str));
		DEBUG_ONLY(tmp_err_hashtab->base = NULL);
		init_hashtab_str(tmp_err_hashtab, (msg_top - msg_beg) * (100.0 / HT_LOAD_FACTOR),
				HASHTAB_NO_COMPACT, HASHTAB_NO_SPARE_TABLE);
		assert(tmp_err_hashtab->base);
		for (msg_info = (err_msg *)msg_beg; msg_info < msg_top; msg_info++)
		{
			key.str.addr = msg_info->tag;
			key.str.len = STRLEN(msg_info->tag);
			COMPUTE_HASH_STR(&key);
			added = add_hashtab_str(tmp_err_hashtab, &key, msg_info, &err_htent);
			assert(added);
			assert(err_htent->value);
			assert(msg_info->tag == ((err_msg *)(err_htent->value))->tag);
		}
		*err_hashtab = tmp_err_hashtab;
	}
	assert(NULL != tmp_err_hashtab);
	/* lookup for the mnemonic */
	key.str.addr = mnemonic_buf;
	key.str.len = mnemonic_len;
	COMPUTE_HASH_STR(&key);
	if (NULL == (err_htent = lookup_hashtab_str(tmp_err_hashtab, &key)))
		return -1;
	msg_info = (err_msg *)(err_htent->value);
	assert(msg_info >= msg_beg && msg_info < msg_top);
	return msg_info - msg_beg;
}

/* Determine if the given msg_id has been explicitly excluded from being able to effectualte an anticipatory freeze */
STATICFNDCL boolean_t	is_message_excluded(int msg_offset)
{
	int		cnt;

	/* See if msg_id is one of the undocumented messages */
	for (cnt = 0; cnt < merrors_ctl.undocmsg_cnt; cnt++)	/* small list of 25-30 messages */
		if (msg_offset == merrors_ctl.undocmsg[cnt])
			return TRUE;
	assert(cnt == merrors_ctl.undocmsg_cnt);
	return FALSE;
}

/* Determine whether a given msg_id qualifies for an anticipatory freeze or not */
boolean_t	is_anticipatory_freeze_needed(sgmnt_addrs *csa, int msg_id)
{
	const err_ctl		*ctl;
	int			idx;
	jnlpool_addrs_ptr_t	local_jnlpool;

	/* Certain error messages should NOT trigger a freeze even if they are so configured in the custom errors file as they might
	 * result in instance freezes that can be set indefinitely. Currently, we know of at least 3 such messages:
	 * 1. ENOSPCQIODEFER and INSTFRZDEFER : To ensure we don't set anticipatory freeze if we don't/can't hold crit
	 *					(due to possible deadlock)
	 * 2. DSKSPCAVAILABLE : To ensure we don't set anticipatory freeze if the disk space becomes available after an initial
	 *			lack of space.
	 * These messages have csa == NULL so they are guarranteed to not trigger a freeze.
	 * However, ENOSPCQIODEFER is returned and later passed to rts_error with a non-NULL csa, so return FALSE in that case.
	 */

	if (!csa || !csa->nl || !csa->hdr || !csa->hdr->freeze_on_fail || (ERR_ENOSPCQIODEFER == msg_id))
		return FALSE;
	assert((ERR_DSKSPCAVAILABLE != msg_id) && (ERR_INSTFRZDEFER != msg_id));
	ctl = err_check(msg_id);
	if (NULL != ctl)
	{
		GET_MSG_IDX(msg_id, ctl, idx);
		local_jnlpool = csa->jnlpool ? csa->jnlpool : jnlpool;
		assert(local_jnlpool && local_jnlpool->jnlpool_ctl);
		assert(idx < ARRAYSIZE(local_jnlpool->jnlpool_ctl->merrors_array));
		if (local_jnlpool->jnlpool_ctl->merrors_array[idx] & AFREEZE_MASK)
			return TRUE;
	}
	return FALSE;
}

/* set the anticipatory freeze in the journal pool */
void		set_anticipatory_freeze(sgmnt_addrs *csa, int msg_id)
{
	boolean_t			was_crit;
	sgmnt_addrs			*repl_csa;
	const err_msg			*msginfo;
	jnlpool_addrs_ptr_t		save_jnlpool;

	assert(is_anticipatory_freeze_needed(csa, msg_id));
	save_jnlpool = jnlpool;
	if (csa->jnlpool && (csa->jnlpool != jnlpool))
		jnlpool = csa->jnlpool;
	assert(jnlpool && jnlpool->jnlpool_ctl);
	assert(jnlpool->jnlpool_dummy_reg);	/* other asserts in is anticipatory_freeze_needed */
	repl_csa = &FILE_INFO(jnlpool->jnlpool_dummy_reg)->s_addrs;
	assert(NULL != repl_csa);
	was_crit = repl_csa->now_crit;
	if (!was_crit)
	{
		if (csa->now_crit)
			grab_lock(jnlpool->jnlpool_dummy_reg, TRUE, GRAB_LOCK_ONLY);
		else if (FALSE == grab_lock(jnlpool->jnlpool_dummy_reg, FALSE, GRAB_LOCK_ONLY))
		{
			MSGID_TO_ERRMSG(msg_id, msginfo);
			send_msg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_INSTFRZDEFER, 4, LEN_AND_STR(msginfo->tag),
				     REG_LEN_STR(csa->region));
			if (save_jnlpool != jnlpool)
				jnlpool = save_jnlpool;
			return;
		}
	}
	/* Now that we hold necessary locks, set the freeze and the comment field */
	jnlpool->jnlpool_ctl->freeze = TRUE;
	GENERATE_INST_FROZEN_COMMENT(jnlpool->jnlpool_ctl->freeze_comment,
			SIZEOF(jnlpool->jnlpool_ctl->freeze_comment), msg_id);
	/* TODO : Do we need a SHM_WRITE_MEMORY_BARRIER ? */
	if (!was_crit)
		rel_lock(jnlpool->jnlpool_dummy_reg);
	if (save_jnlpool != jnlpool)
		jnlpool = save_jnlpool;
}

/* initialize jnlpool_ctl->merrors_array to set up the list of errors that should trigger anticipatory freeze errors */
boolean_t		init_anticipatory_freeze_errors()
{
	int				idx, save_errno, status, mnemonic_len, offset, line_no;
	FILE				*handle;
	char				*fgets_rc;
	char				buff[MAX_READ_SZ], mnemonic_buf[MAX_TAG_LEN];
	char				*buffptr, *buff_top, *errptr, *errptr_top;
	mstr				custom_err_file;
	hash_table_str			*err_hashtab = NULL;
	DCL_THREADGBL_ACCESS;

	SETUP_THREADGBL_ACCESS;
	/* TODO : Currently, we process errors that belong to merrors[] as those are the ones related to database/journal. Need
	 * to check if cmerrors/cmierrors also need to be included in this list or not.
	 */
	assert(IS_MUPIP_IMAGE); 				/* is_src_server is not initialized at this point */
	/* invoke when not previously initialized */
	assert(jnlpool && jnlpool->jnlpool_ctl && !jnlpool->jnlpool_ctl->instfreeze_environ_inited);
	assert(holds_sem[SOURCE][JNL_POOL_ACCESS_SEM]);		/* should hold journal pool access control semaphore */
	/* Now, read the custom errors file and populate the journal pool */
	custom_err_file = TREF(gtm_custom_errors);
	Fopen(handle, custom_err_file.addr, "r");
	if (NULL == handle)
	{
		save_errno = errno;
		send_msg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_CUSTOMFILOPERR, 4, LEN_AND_LIT("fopen"), custom_err_file.len,
				custom_err_file.addr, save_errno);
		gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_CUSTOMFILOPERR, 4, LEN_AND_LIT("fopen"), custom_err_file.len,
				custom_err_file.addr, save_errno);
		return FALSE;
	}
	line_no = 0;
	/* The code below parses a custom errors file in the following format.
	 *
	 * file		::= line*
	 * line		::= mnemonic SPACE* comment? EOL |
	 * 		    comment EOL
	 * mnemonic	::= ALNUM+
	 * comment	::= COMMENT_DELIMITER ANY*
	 *
	 * SPACE		::= any ASCII white space character
	 * COMMENT_DELIMITER	::= ';'
	 * ANY			::= any ASCII character except end of line
	 * EOL			::= ASCII end of line character
	 * ALNUM		::= any ASCII alphanumeric character
	 *
	 * NOTES:
	 *	"*" denotes zero-or-more of the previous item
	 *	"?" denotes zero-or-one of the previous item
	 *	"+" denotes one-or-more of the previous item
	 *	"|" denotes multiple alternatives
	 * 	The mnemonic must match an entry in the GT.M error message list.
	 *	Anything between the COMMENT_DELIMITER and EOL is ignored.
	 *	Each iteration of the loop parses one line.
	 */
	while (TRUE)
	{
		FGETS_FILE(buff, MAX_READ_SZ, handle, fgets_rc);
		line_no++;
		if (NULL == fgets_rc)
			break;
		buffptr = buff;
		buff_top = buffptr + STRLEN(buff);
		errptr = &mnemonic_buf[0];
		errptr_top = errptr + MAX_TAG_LEN;
		/* The first character has to be alpha-numeric or a comment */
		if (!ISALNUM_ASCII(*buffptr) && (COMMENT_DELIMITER != *buffptr))
		{
			send_msg_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_CUSTERRSYNTAX, 3, custom_err_file.len, custom_err_file.addr,
					line_no, ERR_TEXT, 2,
					LEN_AND_LIT("First character should be comment (;) or alpha numeric"));
			gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_CUSTERRSYNTAX, 3, custom_err_file.len, custom_err_file.addr,
					line_no, ERR_TEXT, 2,
					LEN_AND_LIT("First character should be comment (;) or alpha numeric"));
			return FALSE;
		}
		while (ISALNUM_ASCII(*buffptr))
		{
			*errptr++ = *buffptr++;
			if (errptr > errptr_top)
			{
				send_msg_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_CUSTERRSYNTAX, 3, custom_err_file.len,
						custom_err_file.addr, line_no, ERR_TEXT, 2, LEN_AND_LIT("Mnemonic too long"));
				gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_CUSTERRSYNTAX, 3, custom_err_file.len,
						custom_err_file.addr, line_no, ERR_TEXT, 2, LEN_AND_LIT("Mnemonic too long"));
				return FALSE;
			}
			assert(buffptr < buff_top); /* errptr > errptr_top should fail before this */
		}
		*errptr = '\0';
		if (0 < (mnemonic_len = (errptr - &mnemonic_buf[0])))
		{	/* Non-empty error mnemonic found; look it up */
			if (-1 == (offset = get_mnemonic_offset(&err_hashtab, mnemonic_buf, mnemonic_len)))
			{
				send_msg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_CUSTERRNOTFND, 2, mnemonic_len, mnemonic_buf);
				gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_CUSTERRNOTFND, 2, mnemonic_len, mnemonic_buf);
				return FALSE;
			}
			if (!is_message_excluded(offset))
				jnlpool->jnlpool_ctl->merrors_array[offset] |= AFREEZE_MASK; /* duplicate entries are not error */
		}
		assert(ISSPACE_ASCII(*buffptr) || (COMMENT_DELIMITER == *buffptr));
		if (EOL_REACHED == (buffptr = scan_space(handle, buff, buffptr, buff_top)))
			continue;
		else if (EOF_REACHED == buffptr)
			break;
		assert(buffptr < buff_top);
		if (COMMENT_DELIMITER != *buffptr)
		{
			send_msg_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_CUSTERRSYNTAX, 3, custom_err_file.len, custom_err_file.addr,
					line_no, ERR_TEXT, 2, LEN_AND_LIT("Unexpected character found after mnemonic"));
			gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_CUSTERRSYNTAX, 3, custom_err_file.len, custom_err_file.addr,
					line_no, ERR_TEXT, 2, LEN_AND_LIT("Unexpected character found after mnemonic"));
			return FALSE;
		}
		/* Need to ignore the rest of the current buffer and exhaust the current line */
		if (NEWLINE != *(buff_top - 1))
			EXHAUST_CURRENT_LINE(buff, handle, fgets_rc);
	}
	if (err_hashtab)
	{
		free_hashtab_str(err_hashtab);
		free(err_hashtab);
	}
	if (!feof(handle))
	{
		save_errno = errno;
		send_msg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_CUSTOMFILOPERR, 4, LEN_AND_LIT("fgets"), custom_err_file.len,
				custom_err_file.addr, save_errno);
		gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_CUSTOMFILOPERR, 4, LEN_AND_LIT("fgets"), custom_err_file.len,
				custom_err_file.addr, save_errno);
		return FALSE;
	}
	FCLOSE(handle, status);
	if (SS_NORMAL != status)
	{
		save_errno = errno;
		send_msg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_CUSTOMFILOPERR, 4, LEN_AND_LIT("fclose"), custom_err_file.len,
				custom_err_file.addr, save_errno);
		gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_CUSTOMFILOPERR, 4, LEN_AND_LIT("fclose"), custom_err_file.len,
				custom_err_file.addr, save_errno);
		return FALSE;
	}
	jnlpool->jnlpool_ctl->instfreeze_environ_inited = TRUE;
	return TRUE;
}

#ifdef DEBUG
void clear_fake_enospc_if_master_dead(void)
{
	gd_addr				*addr_ptr;
	gd_region			*r_top, *r_local;
	sgmnt_addrs			*csa;

	assert(!multi_thread_in_use);	/* fake-enospc would not have been set if in threaded-code */
	assert(jnlpool && jnlpool->jnlpool_ctl);
	if((jnlpool->jnlpool_ctl->jnlpool_creator_pid != process_id)
		&& !is_proc_alive(jnlpool->jnlpool_ctl->jnlpool_creator_pid, 0))
	{
		for (addr_ptr = get_next_gdr(NULL); addr_ptr; addr_ptr = get_next_gdr(addr_ptr))
		{
			for (r_local = addr_ptr->regions, r_top = r_local + addr_ptr->n_regions; r_local < r_top; r_local++)
			{
				if (!IS_REG_BG_OR_MM(r_local))
					continue;
				csa = REG2CSA(r_local);
				if ((NULL != csa) && (NULL != csa->nl))
					if (csa->nl->fake_db_enospc || csa->nl->fake_jnl_enospc)
					{
						csa->nl->fake_db_enospc = FALSE;
						csa->nl->fake_jnl_enospc = FALSE;
						send_msg_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_TEXT, 2, DB_LEN_STR(r_local), ERR_TEXT,
							     2, LEN_AND_LIT("Resetting fake_db_enospc and fake_jnl_enospc because "
									    "fake ENOSPC master is dead"));
					}
			}
		}
	}
}
#endif