Coverage Report

Created: 2026-09-14 06:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/hdf5/src/H5C.c
Line
Count
Source
1
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2
 * Copyright by The HDF Group.                                               *
3
 * All rights reserved.                                                      *
4
 *                                                                           *
5
 * This file is part of HDF5.  The full HDF5 copyright notice, including     *
6
 * terms governing use, modification, and redistribution, is contained in    *
7
 * the LICENSE file, which can be found at the root of the source code       *
8
 * distribution tree, or in https://www.hdfgroup.org/licenses.               *
9
 * If you do not have access to either file, you may request a copy from     *
10
 * help@hdfgroup.org.                                                        *
11
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
12
13
/*-------------------------------------------------------------------------
14
 *
15
 * Created:     H5C.c
16
 *
17
 * Purpose:     Functions in this file implement a generic cache for
18
 *              things which exist on disk, and which may be
19
 *              unambiguously referenced by their disk addresses.
20
 *
21
 *    For a detailed overview of the cache, please see the
22
 *    header comment for H5C_t in H5Cpkg.h.
23
 *
24
 *-------------------------------------------------------------------------
25
 */
26
27
/**************************************************************************
28
 *
29
 *                To Do:
30
 *
31
 *    Code Changes:
32
 *
33
 *   - Change protect/unprotect to lock/unlock.
34
 *
35
 *     - Flush entries in increasing address order in
36
 *       H5C__make_space_in_cache().
37
 *
38
 *     - Also in H5C__make_space_in_cache(), use high and low water marks
39
 *       to reduce the number of I/O calls.
40
 *
41
 *     - When flushing, attempt to combine contiguous entries to reduce
42
 *       I/O overhead.  Can't do this just yet as some entries are not
43
 *       contiguous.  Do this in parallel only or in serial as well?
44
 *
45
 *   - Fix nodes in memory to point directly to the skip list node from
46
 *         the LRU list, eliminating skip list lookups when evicting objects
47
 *         from the cache.
48
 *
49
 **************************************************************************/
50
51
/****************/
52
/* Module Setup */
53
/****************/
54
55
#include "H5Cmodule.h" /* This source code file is part of the H5C module */
56
#define H5F_FRIEND     /* suppress error about including H5Fpkg  */
57
58
/***********/
59
/* Headers */
60
/***********/
61
#include "H5private.h"   /* Generic Functions            */
62
#include "H5ACprivate.h" /* Metadata cache               */
63
#include "H5Cpkg.h"      /* Cache                        */
64
#include "H5Eprivate.h"  /* Error handling               */
65
#include "H5Fpkg.h"      /* Files                        */
66
#include "H5FLprivate.h" /* Free Lists                   */
67
#include "H5MFprivate.h" /* File memory management       */
68
#include "H5MMprivate.h" /* Memory management            */
69
#include "H5SLprivate.h" /* Skip Lists                               */
70
71
/****************/
72
/* Local Macros */
73
/****************/
74
75
/******************/
76
/* Local Typedefs */
77
/******************/
78
79
/********************/
80
/* Local Prototypes */
81
/********************/
82
83
/*********************/
84
/* Package Variables */
85
/*********************/
86
87
/* Package initialization variable */
88
bool H5_PKG_INIT_VAR = false;
89
90
/* Declare a free list to manage the tag info struct */
91
H5FL_DEFINE(H5C_tag_info_t);
92
93
/*****************************/
94
/* Library Private Variables */
95
/*****************************/
96
97
/*******************/
98
/* Local Variables */
99
/*******************/
100
101
/* Declare a free list to manage the H5C_t struct */
102
H5FL_DEFINE_STATIC(H5C_t);
103
104
/*-------------------------------------------------------------------------
105
 * Function:    H5C_create
106
 *
107
 * Purpose:     Allocate, initialize, and return the address of a new
108
 *        instance of H5C_t.
109
 *
110
 *        In general, the max_cache_size parameter must be positive,
111
 *        and the min_clean_size parameter must lie in the closed
112
 *        interval [0, max_cache_size].
113
 *
114
 *        The check_write_permitted parameter must either be NULL,
115
 *        or point to a function of type H5C_write_permitted_func_t.
116
 *        If it is NULL, the cache will use the write_permitted
117
 *        flag to determine whether writes are permitted.
118
 *
119
 * Return:      Success:        Pointer to the new instance.
120
 *              Failure:        NULL
121
 *
122
 *-------------------------------------------------------------------------
123
 */
124
H5C_t *
125
H5C_create(size_t max_cache_size, size_t min_clean_size, int max_type_id,
126
           const H5C_class_t *const *class_table_ptr, H5C_write_permitted_func_t check_write_permitted,
127
           bool write_permitted, H5C_log_flush_func_t log_flush, void *aux_ptr)
128
23
{
129
23
    int    i;
130
23
    H5C_t *cache_ptr = NULL;
131
23
    H5C_t *ret_value = NULL; /* Return value */
132
133
23
    FUNC_ENTER_NOAPI(NULL)
134
135
23
    assert(max_cache_size >= H5C__MIN_MAX_CACHE_SIZE);
136
23
    assert(max_cache_size <= H5C__MAX_MAX_CACHE_SIZE);
137
23
    assert(min_clean_size <= max_cache_size);
138
139
23
    assert(max_type_id >= 0);
140
23
    assert(max_type_id < H5C__MAX_NUM_TYPE_IDS);
141
23
    assert(class_table_ptr);
142
143
713
    for (i = 0; i <= max_type_id; i++) {
144
690
        assert((class_table_ptr)[i]);
145
690
        assert(strlen((class_table_ptr)[i]->name) > 0);
146
690
    } /* end for */
147
148
23
    if (NULL == (cache_ptr = H5FL_CALLOC(H5C_t)))
149
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed");
150
151
23
    if (NULL == (cache_ptr->slist_ptr = H5SL_create(H5SL_TYPE_HADDR, NULL)))
152
0
        HGOTO_ERROR(H5E_CACHE, H5E_CANTCREATE, NULL, "can't create skip list");
153
154
23
    cache_ptr->tag_list = NULL;
155
156
    /* If we get this far, we should succeed.  Go ahead and initialize all
157
     * the fields.
158
     */
159
160
23
    cache_ptr->flush_in_progress = false;
161
162
23
    if (NULL == (cache_ptr->log_info = (H5C_log_info_t *)H5MM_calloc(sizeof(H5C_log_info_t))))
163
0
        HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, NULL, "memory allocation failed");
164
165
23
    cache_ptr->aux_ptr = aux_ptr;
166
167
23
    cache_ptr->max_type_id = max_type_id;
168
169
23
    cache_ptr->class_table_ptr = class_table_ptr;
170
171
23
    cache_ptr->max_cache_size = max_cache_size;
172
23
    cache_ptr->min_clean_size = min_clean_size;
173
174
23
    cache_ptr->check_write_permitted = check_write_permitted;
175
23
    cache_ptr->write_permitted       = write_permitted;
176
177
23
    cache_ptr->log_flush = log_flush;
178
179
23
    cache_ptr->evictions_enabled      = true;
180
23
    cache_ptr->close_warning_received = false;
181
182
23
    cache_ptr->index_len        = 0;
183
23
    cache_ptr->index_size       = (size_t)0;
184
23
    cache_ptr->clean_index_size = (size_t)0;
185
23
    cache_ptr->dirty_index_size = (size_t)0;
186
187
161
    for (i = 0; i < H5C_RING_NTYPES; i++) {
188
138
        cache_ptr->index_ring_len[i]        = 0;
189
138
        cache_ptr->index_ring_size[i]       = (size_t)0;
190
138
        cache_ptr->clean_index_ring_size[i] = (size_t)0;
191
138
        cache_ptr->dirty_index_ring_size[i] = (size_t)0;
192
193
138
        cache_ptr->slist_ring_len[i]  = 0;
194
138
        cache_ptr->slist_ring_size[i] = (size_t)0;
195
138
    } /* end for */
196
197
1.50M
    for (i = 0; i < H5C__HASH_TABLE_LEN; i++)
198
1.50M
        (cache_ptr->index)[i] = NULL;
199
200
23
    cache_ptr->il_len  = 0;
201
23
    cache_ptr->il_size = (size_t)0;
202
23
    cache_ptr->il_head = NULL;
203
23
    cache_ptr->il_tail = NULL;
204
205
    /* Tagging Field Initializations */
206
23
    cache_ptr->ignore_tags     = false;
207
23
    cache_ptr->num_objs_corked = 0;
208
209
    /* slist field initializations */
210
23
    cache_ptr->slist_enabled = false;
211
23
    cache_ptr->slist_changed = false;
212
23
    cache_ptr->slist_len     = 0;
213
23
    cache_ptr->slist_size    = (size_t)0;
214
215
    /* slist_ring_len, slist_ring_size, and
216
     * slist_ptr initialized above.
217
     */
218
219
#ifdef H5C_DO_SANITY_CHECKS
220
    cache_ptr->slist_len_increase  = 0;
221
    cache_ptr->slist_size_increase = 0;
222
#endif /* H5C_DO_SANITY_CHECKS */
223
224
23
    cache_ptr->entries_removed_counter   = 0;
225
23
    cache_ptr->last_entry_removed_ptr    = NULL;
226
23
    cache_ptr->entry_watched_for_removal = NULL;
227
228
23
    cache_ptr->pl_len      = 0;
229
23
    cache_ptr->pl_size     = (size_t)0;
230
23
    cache_ptr->pl_head_ptr = NULL;
231
23
    cache_ptr->pl_tail_ptr = NULL;
232
233
23
    cache_ptr->pel_len      = 0;
234
23
    cache_ptr->pel_size     = (size_t)0;
235
23
    cache_ptr->pel_head_ptr = NULL;
236
23
    cache_ptr->pel_tail_ptr = NULL;
237
238
23
    cache_ptr->LRU_list_len  = 0;
239
23
    cache_ptr->LRU_list_size = (size_t)0;
240
23
    cache_ptr->LRU_head_ptr  = NULL;
241
23
    cache_ptr->LRU_tail_ptr  = NULL;
242
243
#ifdef H5_HAVE_PARALLEL
244
    cache_ptr->coll_list_len   = 0;
245
    cache_ptr->coll_list_size  = (size_t)0;
246
    cache_ptr->coll_head_ptr   = NULL;
247
    cache_ptr->coll_tail_ptr   = NULL;
248
    cache_ptr->coll_write_list = NULL;
249
#endif /* H5_HAVE_PARALLEL */
250
251
#if H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS
252
    cache_ptr->cLRU_list_len  = 0;
253
    cache_ptr->cLRU_list_size = (size_t)0;
254
    cache_ptr->cLRU_head_ptr  = NULL;
255
    cache_ptr->cLRU_tail_ptr  = NULL;
256
257
    cache_ptr->dLRU_list_len  = 0;
258
    cache_ptr->dLRU_list_size = (size_t)0;
259
    cache_ptr->dLRU_head_ptr  = NULL;
260
    cache_ptr->dLRU_tail_ptr  = NULL;
261
#endif /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */
262
263
23
    cache_ptr->size_increase_possible        = false;
264
23
    cache_ptr->flash_size_increase_possible  = false;
265
23
    cache_ptr->flash_size_increase_threshold = 0;
266
23
    cache_ptr->size_decrease_possible        = false;
267
23
    cache_ptr->resize_enabled                = false;
268
23
    cache_ptr->cache_full                    = false;
269
23
    cache_ptr->size_decreased                = false;
270
23
    cache_ptr->resize_in_progress            = false;
271
23
    cache_ptr->msic_in_progress              = false;
272
273
23
    cache_ptr->resize_ctl.version            = H5C__CURR_AUTO_SIZE_CTL_VER;
274
23
    cache_ptr->resize_ctl.rpt_fcn            = NULL;
275
23
    cache_ptr->resize_ctl.set_initial_size   = false;
276
23
    cache_ptr->resize_ctl.initial_size       = H5C__DEF_AR_INIT_SIZE;
277
23
    cache_ptr->resize_ctl.min_clean_fraction = H5C__DEF_AR_MIN_CLEAN_FRAC;
278
23
    cache_ptr->resize_ctl.max_size           = H5C__DEF_AR_MAX_SIZE;
279
23
    cache_ptr->resize_ctl.min_size           = H5C__DEF_AR_MIN_SIZE;
280
23
    cache_ptr->resize_ctl.epoch_length       = H5C__DEF_AR_EPOCH_LENGTH;
281
282
23
    cache_ptr->resize_ctl.incr_mode           = H5C_incr__off;
283
23
    cache_ptr->resize_ctl.lower_hr_threshold  = H5C__DEF_AR_LOWER_THRESHHOLD;
284
23
    cache_ptr->resize_ctl.increment           = H5C__DEF_AR_INCREMENT;
285
23
    cache_ptr->resize_ctl.apply_max_increment = true;
286
23
    cache_ptr->resize_ctl.max_increment       = H5C__DEF_AR_MAX_INCREMENT;
287
288
23
    cache_ptr->resize_ctl.flash_incr_mode = H5C_flash_incr__off;
289
23
    cache_ptr->resize_ctl.flash_multiple  = 1.0;
290
23
    cache_ptr->resize_ctl.flash_threshold = 0.25;
291
292
23
    cache_ptr->resize_ctl.decr_mode              = H5C_decr__off;
293
23
    cache_ptr->resize_ctl.upper_hr_threshold     = H5C__DEF_AR_UPPER_THRESHHOLD;
294
23
    cache_ptr->resize_ctl.decrement              = H5C__DEF_AR_DECREMENT;
295
23
    cache_ptr->resize_ctl.apply_max_decrement    = true;
296
23
    cache_ptr->resize_ctl.max_decrement          = H5C__DEF_AR_MAX_DECREMENT;
297
23
    cache_ptr->resize_ctl.epochs_before_eviction = H5C__DEF_AR_EPCHS_B4_EVICT;
298
23
    cache_ptr->resize_ctl.apply_empty_reserve    = true;
299
23
    cache_ptr->resize_ctl.empty_reserve          = H5C__DEF_AR_EMPTY_RESERVE;
300
301
23
    cache_ptr->epoch_markers_active = 0;
302
303
    /* no need to initialize the ring buffer itself */
304
23
    cache_ptr->epoch_marker_ringbuf_first = 1;
305
23
    cache_ptr->epoch_marker_ringbuf_last  = 0;
306
23
    cache_ptr->epoch_marker_ringbuf_size  = 0;
307
308
    /* Initialize all epoch marker entries' fields to zero/false/NULL */
309
23
    memset(cache_ptr->epoch_markers, 0, sizeof(cache_ptr->epoch_markers));
310
311
    /* Set non-zero/false/NULL fields for epoch markers */
312
253
    for (i = 0; i < H5C__MAX_EPOCH_MARKERS; i++) {
313
230
        ((cache_ptr->epoch_markers)[i]).addr = (haddr_t)i;
314
230
        ((cache_ptr->epoch_markers)[i]).type = H5AC_EPOCH_MARKER;
315
230
    }
316
317
    /* Initialize cache image generation on file close related fields.
318
     * Initial value of image_ctl must match H5C__DEFAULT_CACHE_IMAGE_CTL
319
     * in H5Cprivate.h.
320
     */
321
23
    cache_ptr->image_ctl.version            = H5C__CURR_CACHE_IMAGE_CTL_VER;
322
23
    cache_ptr->image_ctl.generate_image     = false;
323
23
    cache_ptr->image_ctl.save_resize_status = false;
324
23
    cache_ptr->image_ctl.entry_ageout       = -1;
325
23
    cache_ptr->image_ctl.flags              = H5C_CI__ALL_FLAGS;
326
327
23
    cache_ptr->serialization_in_progress = false;
328
23
    cache_ptr->load_image                = false;
329
23
    cache_ptr->image_loaded              = false;
330
23
    cache_ptr->delete_image              = false;
331
23
    cache_ptr->image_addr                = HADDR_UNDEF;
332
23
    cache_ptr->image_len                 = 0;
333
23
    cache_ptr->image_data_len            = 0;
334
335
23
    cache_ptr->entries_loaded_counter         = 0;
336
23
    cache_ptr->entries_inserted_counter       = 0;
337
23
    cache_ptr->entries_relocated_counter      = 0;
338
23
    cache_ptr->entry_fd_height_change_counter = 0;
339
340
23
    cache_ptr->num_entries_in_image = 0;
341
23
    cache_ptr->image_entries        = NULL;
342
23
    cache_ptr->image_buffer         = NULL;
343
344
    /* initialize free space manager related fields: */
345
23
    cache_ptr->rdfsm_settled = false;
346
23
    cache_ptr->mdfsm_settled = false;
347
348
23
    if (H5C_reset_cache_hit_rate_stats(cache_ptr) < 0)
349
        /* this should be impossible... */
350
0
        HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, NULL, "H5C_reset_cache_hit_rate_stats failed");
351
352
23
    H5C_stats__reset(cache_ptr);
353
354
23
    cache_ptr->prefix[0] = '\0'; /* empty string */
355
356
#ifndef NDEBUG
357
    cache_ptr->get_entry_ptr_from_addr_counter = 0;
358
#endif
359
360
    /* Set return value */
361
23
    ret_value = cache_ptr;
362
363
23
done:
364
23
    if (NULL == ret_value) {
365
0
        if (cache_ptr != NULL) {
366
0
            if (cache_ptr->slist_ptr != NULL)
367
0
                H5SL_close(cache_ptr->slist_ptr);
368
369
0
            HASH_CLEAR(hh, cache_ptr->tag_list);
370
0
            cache_ptr->tag_list = NULL;
371
372
0
            if (cache_ptr->log_info != NULL)
373
0
                H5MM_xfree(cache_ptr->log_info);
374
375
0
            cache_ptr = H5FL_FREE(H5C_t, cache_ptr);
376
0
        }
377
0
    }
378
379
23
    FUNC_LEAVE_NOAPI(ret_value)
380
23
} /* H5C_create() */
381
382
/*-------------------------------------------------------------------------
383
 * Function:    H5C_prep_for_file_close
384
 *
385
 * Purpose:     This function should be called just prior to the cache
386
 *        flushes at file close.  There should be no protected
387
 *        entries in the cache at this point.
388
 *
389
 * Return:      Non-negative on success/Negative on failure
390
 *
391
 *-------------------------------------------------------------------------
392
 */
393
herr_t
394
H5C_prep_for_file_close(H5F_t *f)
395
23
{
396
23
    H5C_t *cache_ptr;
397
23
    bool   image_generated = false;   /* Whether a cache image was generated */
398
23
    herr_t ret_value       = SUCCEED; /* Return value */
399
400
23
    FUNC_ENTER_NOAPI(FAIL)
401
402
    /* Sanity checks */
403
23
    assert(f);
404
23
    assert(f->shared);
405
23
    assert(f->shared->cache);
406
23
    cache_ptr = f->shared->cache;
407
23
    assert(cache_ptr);
408
409
    /* It is possible to receive the close warning more than once */
410
23
    if (cache_ptr->close_warning_received)
411
0
        HGOTO_DONE(SUCCEED);
412
23
    cache_ptr->close_warning_received = true;
413
414
    /* Make certain there aren't any protected entries */
415
23
    assert(cache_ptr->pl_len == 0);
416
417
    /* Prepare cache image */
418
23
    if (H5C__prep_image_for_file_close(f, &image_generated) < 0)
419
0
        HGOTO_ERROR(H5E_CACHE, H5E_CANTCREATE, FAIL, "can't create cache image");
420
421
#ifdef H5_HAVE_PARALLEL
422
    if ((H5F_INTENT(f) & H5F_ACC_RDWR) && !image_generated && cache_ptr->aux_ptr != NULL &&
423
        f->shared->fs_persist) {
424
        /* If persistent free space managers are enabled, flushing the
425
         * metadata cache may result in the deletion, insertion, and/or
426
         * dirtying of entries.
427
         *
428
         * This is a problem in PHDF5, as it breaks two invariants of
429
         * our management of the metadata cache across all processes:
430
         *
431
         * 1) Entries will not be dirtied, deleted, inserted, or moved
432
         *    during flush in the parallel case.
433
         *
434
         * 2) All processes contain the same set of dirty metadata
435
         *    entries on entry to a sync point.
436
         *
437
         * To solve this problem for the persistent free space managers,
438
         * serialize the metadata cache on all processes prior to the
439
         * first sync point on file shutdown.  The shutdown warning is
440
         * a convenient location for this call.
441
         *
442
         * This is sufficient since:
443
         *
444
         * 1) FSM settle routines are only invoked on file close.  Since
445
         *    serialization make the same settle calls as flush on file
446
         *    close, and since the close warning is issued after all
447
         *    non FSM related space allocations and just before the
448
         *    first sync point on close, this call will leave the caches
449
         *    in a consistent state across the processes if they were
450
         *    consistent before.
451
         *
452
         * 2) Since the FSM settle routines are only invoked once during
453
         *    file close, invoking them now will prevent their invocation
454
         *    during a flush, and thus avoid any resulting entry dirties,
455
         *    deletions, insertion, or moves during the flush.
456
         */
457
        if (H5C__serialize_cache(f) < 0)
458
            HGOTO_ERROR(H5E_CACHE, H5E_CANTSERIALIZE, FAIL, "serialization of the cache failed");
459
    }  /* end if */
460
#endif /* H5_HAVE_PARALLEL */
461
462
23
done:
463
23
    FUNC_LEAVE_NOAPI(ret_value)
464
23
} /* H5C_prep_for_file_close() */
465
466
/*-------------------------------------------------------------------------
467
 * Function:    H5C_dest
468
 *
469
 * Purpose:     Flush all data to disk and destroy the cache.
470
 *
471
 *              This function fails if any object are protected since the
472
 *              resulting file might not be consistent.
473
 *
474
 * Note:        *cache_ptr has been freed upon successful return.
475
 *
476
 * Return:      Non-negative on success/Negative on failure
477
 *
478
 *-------------------------------------------------------------------------
479
 */
480
herr_t
481
H5C_dest(H5F_t *f)
482
23
{
483
23
    H5C_t          *cache_ptr = f->shared->cache;
484
23
    H5C_tag_info_t *item      = NULL;
485
23
    H5C_tag_info_t *tmp       = NULL;
486
23
    herr_t          ret_value = SUCCEED; /* Return value */
487
488
23
    FUNC_ENTER_NOAPI(FAIL)
489
490
    /* Sanity check */
491
23
    assert(cache_ptr);
492
23
    assert(cache_ptr->close_warning_received);
493
494
#if H5AC_DUMP_IMAGE_STATS_ON_CLOSE
495
    if (H5C__image_stats(cache_ptr, true) < 0)
496
        HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Can't display cache image stats");
497
#endif /* H5AC_DUMP_IMAGE_STATS_ON_CLOSE */
498
499
    /* Enable the slist, as it is needed in the flush */
500
23
    if (H5C_set_slist_enabled(f->shared->cache, true, true) < 0)
501
0
        HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "set slist enabled failed");
502
503
    /* Flush and invalidate all cache entries */
504
23
    if (H5C__flush_invalidate_cache(f, H5C__NO_FLAGS_SET) < 0)
505
0
        HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to flush cache");
506
507
    /* Generate & write cache image if requested */
508
23
    if (cache_ptr->image_ctl.generate_image)
509
0
        if (H5C__generate_cache_image(f, cache_ptr) < 0)
510
0
            HGOTO_ERROR(H5E_CACHE, H5E_CANTCREATE, FAIL, "Can't generate metadata cache image");
511
512
    /* Question: Is it possible for cache_ptr->slist be non-null at this
513
     *           point?  If no, shouldn't this if statement be an assert?
514
     */
515
23
    if (cache_ptr->slist_ptr != NULL) {
516
23
        assert(cache_ptr->slist_len == 0);
517
23
        assert(cache_ptr->slist_size == 0);
518
519
23
        H5SL_close(cache_ptr->slist_ptr);
520
23
        cache_ptr->slist_ptr = NULL;
521
23
    }
522
523
23
    HASH_ITER(hh, cache_ptr->tag_list, item, tmp)
524
0
    {
525
0
        HASH_DELETE(hh, cache_ptr->tag_list, item);
526
0
        item = H5FL_FREE(H5C_tag_info_t, item);
527
0
    }
528
529
23
    if (cache_ptr->log_info != NULL)
530
23
        H5MM_xfree(cache_ptr->log_info);
531
532
#ifdef H5C_DO_SANITY_CHECKS
533
    if (cache_ptr->get_entry_ptr_from_addr_counter > 0)
534
        fprintf(stdout, "*** %" PRId64 " calls to H5C_get_entry_ptr_from_add(). ***\n",
535
                cache_ptr->get_entry_ptr_from_addr_counter);
536
#endif /* H5C_DO_SANITY_CHECKS */
537
538
23
    cache_ptr = H5FL_FREE(H5C_t, cache_ptr);
539
540
23
done:
541
23
    if (ret_value < 0 && cache_ptr && cache_ptr->slist_ptr)
542
        /* Arguably, it shouldn't be necessary to re-enable the slist after
543
         * the call to H5C__flush_invalidate_cache(), as the metadata cache
544
         * should be discarded.  However, in the test code, we make multiple
545
         * calls to H5C_dest().  Thus we re-enable the slist on failure if it
546
         * and the cache still exist.  JRM -- 5/15/20
547
         */
548
0
        if (H5C_set_slist_enabled(f->shared->cache, false, false) < 0)
549
0
            HDONE_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "disable slist on flush dest failure failed");
550
551
23
    FUNC_LEAVE_NOAPI(ret_value)
552
23
} /* H5C_dest() */
553
554
/*-------------------------------------------------------------------------
555
 * Function:    H5C_evict
556
 *
557
 * Purpose:     Evict all except pinned entries in the cache
558
 *
559
 * Return:      Non-negative on success/Negative on failure
560
 *
561
 *-------------------------------------------------------------------------
562
 */
563
herr_t
564
H5C_evict(H5F_t *f)
565
0
{
566
0
    herr_t ret_value = SUCCEED; /* Return value */
567
568
0
    FUNC_ENTER_NOAPI(FAIL)
569
570
    /* Sanity check */
571
0
    assert(f);
572
573
    /* Enable the slist, as it is needed in the flush */
574
0
    if (H5C_set_slist_enabled(f->shared->cache, true, true) < 0)
575
0
        HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "set slist enabled failed");
576
577
    /* Flush and invalidate all cache entries except the pinned entries */
578
0
    if (H5C__flush_invalidate_cache(f, H5C__EVICT_ALLOW_LAST_PINS_FLAG) < 0)
579
0
        HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to evict entries in the cache");
580
581
    /* Disable the slist */
582
0
    if (H5C_set_slist_enabled(f->shared->cache, false, false) < 0)
583
0
        HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "set slist disabled failed");
584
585
0
done:
586
0
    FUNC_LEAVE_NOAPI(ret_value)
587
0
} /* H5C_evict() */
588
589
/*-------------------------------------------------------------------------
590
 * Function:    H5C_flush_cache
591
 *
592
 * Purpose:    Flush (and possibly destroy) the entries contained in the
593
 *        specified cache.
594
 *
595
 *        If the cache contains protected entries, the function will
596
 *        fail, as protected entries cannot be flushed.  However
597
 *        all unprotected entries should be flushed before the
598
 *        function returns failure.
599
 *
600
 * Return:      Non-negative on success/Negative on failure or if there was
601
 *        a request to flush all items and an entry was protected.
602
 *
603
 *-------------------------------------------------------------------------
604
 */
605
herr_t
606
H5C_flush_cache(H5F_t *f, unsigned flags)
607
4
{
608
#ifdef H5C_DO_SANITY_CHECKS
609
    int      i;
610
    uint32_t index_len        = 0;
611
    size_t   index_size       = (size_t)0;
612
    size_t   clean_index_size = (size_t)0;
613
    size_t   dirty_index_size = (size_t)0;
614
    size_t   slist_size       = (size_t)0;
615
    uint32_t slist_len        = 0;
616
#endif /* H5C_DO_SANITY_CHECKS */
617
4
    H5C_ring_t ring;
618
4
    H5C_t     *cache_ptr;
619
4
    bool       destroy;
620
4
    herr_t     ret_value = SUCCEED;
621
622
4
    FUNC_ENTER_NOAPI(FAIL)
623
624
4
    assert(f);
625
4
    assert(f->shared);
626
4
    cache_ptr = f->shared->cache;
627
4
    assert(cache_ptr);
628
4
    assert(cache_ptr->slist_ptr);
629
630
#ifdef H5C_DO_SANITY_CHECKS
631
    assert(cache_ptr->index_ring_len[H5C_RING_UNDEFINED] == 0);
632
    assert(cache_ptr->index_ring_size[H5C_RING_UNDEFINED] == (size_t)0);
633
    assert(cache_ptr->clean_index_ring_size[H5C_RING_UNDEFINED] == (size_t)0);
634
    assert(cache_ptr->dirty_index_ring_size[H5C_RING_UNDEFINED] == (size_t)0);
635
    assert(cache_ptr->slist_ring_len[H5C_RING_UNDEFINED] == 0);
636
    assert(cache_ptr->slist_ring_size[H5C_RING_UNDEFINED] == (size_t)0);
637
638
    for (i = H5C_RING_USER; i < H5C_RING_NTYPES; i++) {
639
        index_len += cache_ptr->index_ring_len[i];
640
        index_size += cache_ptr->index_ring_size[i];
641
        clean_index_size += cache_ptr->clean_index_ring_size[i];
642
        dirty_index_size += cache_ptr->dirty_index_ring_size[i];
643
644
        slist_len += cache_ptr->slist_ring_len[i];
645
        slist_size += cache_ptr->slist_ring_size[i];
646
    } /* end for */
647
648
    assert(cache_ptr->index_len == index_len);
649
    assert(cache_ptr->index_size == index_size);
650
    assert(cache_ptr->clean_index_size == clean_index_size);
651
    assert(cache_ptr->dirty_index_size == dirty_index_size);
652
    assert(cache_ptr->slist_len == slist_len);
653
    assert(cache_ptr->slist_size == slist_size);
654
#endif /* H5C_DO_SANITY_CHECKS */
655
656
#ifdef H5C_DO_EXTREME_SANITY_CHECKS
657
    if (H5C__validate_protected_entry_list(cache_ptr) < 0 || H5C__validate_pinned_entry_list(cache_ptr) < 0 ||
658
        H5C__validate_lru_list(cache_ptr) < 0)
659
        HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on entry");
660
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
661
662
4
    destroy = ((flags & H5C__FLUSH_INVALIDATE_FLAG) != 0);
663
4
    assert(!(destroy && ((flags & H5C__FLUSH_IGNORE_PROTECTED_FLAG) != 0)));
664
4
    assert(!(cache_ptr->flush_in_progress));
665
666
4
    cache_ptr->flush_in_progress = true;
667
668
4
    if (destroy) {
669
0
        if (H5C__flush_invalidate_cache(f, flags) < 0)
670
0
            HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "flush invalidate failed");
671
0
    } /* end if */
672
4
    else {
673
        /* flush each ring, starting from the outermost ring and
674
         * working inward.
675
         */
676
4
        ring = H5C_RING_USER;
677
24
        while (ring < H5C_RING_NTYPES) {
678
            /* Only call the free space manager settle routines when close
679
             * warning has been received.
680
             */
681
20
            if (cache_ptr->close_warning_received) {
682
10
                switch (ring) {
683
2
                    case H5C_RING_USER:
684
2
                        break;
685
686
2
                    case H5C_RING_RDFSM:
687
                        /* Settle raw data FSM */
688
2
                        if (!cache_ptr->rdfsm_settled)
689
2
                            if (H5MF_settle_raw_data_fsm(f, &cache_ptr->rdfsm_settled) < 0)
690
0
                                HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "RD FSM settle failed");
691
2
                        break;
692
693
2
                    case H5C_RING_MDFSM:
694
                        /* Settle metadata FSM */
695
2
                        if (!cache_ptr->mdfsm_settled)
696
2
                            if (H5MF_settle_meta_data_fsm(f, &cache_ptr->mdfsm_settled) < 0)
697
0
                                HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "MD FSM settle failed");
698
2
                        break;
699
700
2
                    case H5C_RING_SBE:
701
4
                    case H5C_RING_SB:
702
4
                        break;
703
704
0
                    default:
705
0
                        HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Unknown ring?!?!");
706
0
                        break;
707
10
                } /* end switch */
708
10
            }     /* end if */
709
710
20
            if (H5C__flush_ring(f, ring, flags) < 0)
711
0
                HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "flush ring failed");
712
20
            ring++;
713
20
        } /* end while */
714
4
    }     /* end else */
715
716
4
done:
717
4
    cache_ptr->flush_in_progress = false;
718
719
4
    FUNC_LEAVE_NOAPI(ret_value)
720
4
} /* H5C_flush_cache() */
721
722
/*-------------------------------------------------------------------------
723
 * Function:    H5C_flush_to_min_clean
724
 *
725
 * Purpose:    Flush dirty entries until the caches min clean size is
726
 *        attained.
727
 *
728
 *        This function is used in the implementation of the
729
 *        metadata cache in PHDF5.  To avoid "messages from the
730
 *        future", the cache on process 0 can't be allowed to
731
 *        flush entries until the other processes have reached
732
 *        the same point in the calculation.  If this constraint
733
 *        is not met, it is possible that the other processes will
734
 *        read metadata generated at a future point in the
735
 *        computation.
736
 *
737
 *
738
 * Return:      Non-negative on success/Negative on failure or if
739
 *        write is not permitted.
740
 *
741
 *-------------------------------------------------------------------------
742
 */
743
herr_t
744
H5C_flush_to_min_clean(H5F_t *f)
745
0
{
746
0
    H5C_t *cache_ptr;
747
0
    bool   write_permitted;
748
0
    herr_t ret_value = SUCCEED;
749
750
0
    FUNC_ENTER_NOAPI(FAIL)
751
752
0
    assert(f);
753
0
    assert(f->shared);
754
0
    cache_ptr = f->shared->cache;
755
0
    assert(cache_ptr);
756
757
0
    if (cache_ptr->check_write_permitted != NULL) {
758
0
        if ((cache_ptr->check_write_permitted)(f, &write_permitted) < 0)
759
0
            HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "can't get write_permitted");
760
0
    } /* end if */
761
0
    else
762
0
        write_permitted = cache_ptr->write_permitted;
763
764
0
    if (!write_permitted)
765
0
        HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "cache write is not permitted!?!");
766
767
0
    if (H5C__make_space_in_cache(f, (size_t)0, write_permitted) < 0)
768
0
        HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "H5C__make_space_in_cache failed");
769
770
0
done:
771
0
    FUNC_LEAVE_NOAPI(ret_value)
772
0
} /* H5C_flush_to_min_clean() */
773
774
/*-------------------------------------------------------------------------
775
 * Function:    H5C_reset_cache_hit_rate_stats()
776
 *
777
 * Purpose:     Reset the cache hit rate computation fields.
778
 *
779
 * Return:      SUCCEED on success, and FAIL on failure.
780
 *
781
 *-------------------------------------------------------------------------
782
 */
783
herr_t
784
H5C_reset_cache_hit_rate_stats(H5C_t *cache_ptr)
785
59
{
786
59
    herr_t ret_value = SUCCEED; /* Return value */
787
788
59
    FUNC_ENTER_NOAPI(FAIL)
789
790
59
    if (cache_ptr == NULL)
791
0
        HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "bad cache_ptr on entry");
792
793
59
    cache_ptr->cache_hits     = 0;
794
59
    cache_ptr->cache_accesses = 0;
795
796
59
done:
797
59
    FUNC_LEAVE_NOAPI(ret_value)
798
59
} /* H5C_reset_cache_hit_rate_stats() */
799
800
/*-------------------------------------------------------------------------
801
 * Function:    H5C_set_cache_auto_resize_config
802
 *
803
 * Purpose:    Set the cache automatic resize configuration to the
804
 *        provided values if they are in range, and fail if they
805
 *        are not.
806
 *
807
 *        If the new configuration enables automatic cache resizing,
808
 *        coerce the cache max size and min clean size into agreement
809
 *        with the new policy and re-set the full cache hit rate
810
 *        stats.
811
 *
812
 * Return:      SUCCEED on success, and FAIL on failure.
813
 *
814
 *-------------------------------------------------------------------------
815
 */
816
herr_t
817
H5C_set_cache_auto_resize_config(H5C_t *cache_ptr, H5C_auto_size_ctl_t *config_ptr)
818
23
{
819
23
    size_t new_max_cache_size;
820
23
    size_t new_min_clean_size;
821
23
    herr_t ret_value = SUCCEED; /* Return value */
822
823
23
    FUNC_ENTER_NOAPI(FAIL)
824
825
23
    if (cache_ptr == NULL)
826
0
        HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "bad cache_ptr on entry");
827
23
    if (config_ptr == NULL)
828
0
        HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "NULL config_ptr on entry");
829
23
    if (config_ptr->version != H5C__CURR_AUTO_SIZE_CTL_VER)
830
0
        HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "unknown config version");
831
832
    /* check general configuration section of the config: */
833
23
    if (H5C_validate_resize_config(config_ptr, H5C_RESIZE_CFG__VALIDATE_GENERAL) < 0)
834
0
        HGOTO_ERROR(H5E_ARGS, H5E_BADRANGE, FAIL, "error in general configuration fields of new config");
835
836
    /* check size increase control fields of the config: */
837
23
    if (H5C_validate_resize_config(config_ptr, H5C_RESIZE_CFG__VALIDATE_INCREMENT) < 0)
838
0
        HGOTO_ERROR(H5E_ARGS, H5E_BADRANGE, FAIL, "error in the size increase control fields of new config");
839
840
    /* check size decrease control fields of the config: */
841
23
    if (H5C_validate_resize_config(config_ptr, H5C_RESIZE_CFG__VALIDATE_DECREMENT) < 0)
842
0
        HGOTO_ERROR(H5E_ARGS, H5E_BADRANGE, FAIL, "error in the size decrease control fields of new config");
843
844
    /* check for conflicts between size increase and size decrease controls: */
845
23
    if (H5C_validate_resize_config(config_ptr, H5C_RESIZE_CFG__VALIDATE_INTERACTIONS) < 0)
846
0
        HGOTO_ERROR(H5E_ARGS, H5E_BADRANGE, FAIL, "conflicting threshold fields in new config");
847
848
    /* will set the increase possible fields to false later if needed */
849
23
    cache_ptr->size_increase_possible       = true;
850
23
    cache_ptr->flash_size_increase_possible = true;
851
23
    cache_ptr->size_decrease_possible       = true;
852
853
23
    switch (config_ptr->incr_mode) {
854
0
        case H5C_incr__off:
855
0
            cache_ptr->size_increase_possible = false;
856
0
            break;
857
858
23
        case H5C_incr__threshold:
859
23
            if ((config_ptr->lower_hr_threshold <= 0.0) || (config_ptr->increment <= 1.0) ||
860
23
                ((config_ptr->apply_max_increment) && (config_ptr->max_increment <= 0)))
861
0
                cache_ptr->size_increase_possible = false;
862
23
            break;
863
864
0
        default: /* should be unreachable */
865
0
            HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Unknown incr_mode?!?!?");
866
23
    } /* end switch */
867
868
    /* logically, this is where configuration for flash cache size increases
869
     * should go.  However, this configuration depends on max_cache_size, so
870
     * we wait until the end of the function, when this field is set.
871
     */
872
873
23
    switch (config_ptr->decr_mode) {
874
0
        case H5C_decr__off:
875
0
            cache_ptr->size_decrease_possible = false;
876
0
            break;
877
878
0
        case H5C_decr__threshold:
879
0
            if (config_ptr->upper_hr_threshold >= 1.0 || config_ptr->decrement >= 1.0 ||
880
0
                (config_ptr->apply_max_decrement && config_ptr->max_decrement <= 0))
881
0
                cache_ptr->size_decrease_possible = false;
882
0
            break;
883
884
0
        case H5C_decr__age_out:
885
0
            if ((config_ptr->apply_empty_reserve && config_ptr->empty_reserve >= 1.0) ||
886
0
                (config_ptr->apply_max_decrement && config_ptr->max_decrement <= 0))
887
0
                cache_ptr->size_decrease_possible = false;
888
0
            break;
889
890
23
        case H5C_decr__age_out_with_threshold:
891
23
            if ((config_ptr->apply_empty_reserve && config_ptr->empty_reserve >= 1.0) ||
892
23
                (config_ptr->apply_max_decrement && config_ptr->max_decrement <= 0) ||
893
23
                config_ptr->upper_hr_threshold >= 1.0)
894
0
                cache_ptr->size_decrease_possible = false;
895
23
            break;
896
897
0
        default: /* should be unreachable */
898
0
            HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Unknown decr_mode?!?!?");
899
23
    } /* end switch */
900
901
23
    if (config_ptr->max_size == config_ptr->min_size) {
902
0
        cache_ptr->size_increase_possible       = false;
903
0
        cache_ptr->flash_size_increase_possible = false;
904
0
        cache_ptr->size_decrease_possible       = false;
905
0
    } /* end if */
906
907
    /* flash_size_increase_possible is intentionally omitted from the
908
     * following:
909
     */
910
23
    cache_ptr->resize_enabled = cache_ptr->size_increase_possible || cache_ptr->size_decrease_possible;
911
23
    cache_ptr->resize_ctl     = *config_ptr;
912
913
    /* Resize the cache to the supplied initial value if requested, or as
914
     * necessary to force it within the bounds of the current automatic
915
     * cache resizing configuration.
916
     *
917
     * Note that the min_clean_fraction may have changed, so we
918
     * go through the exercise even if the current size is within
919
     * range and an initial size has not been provided.
920
     */
921
23
    if (cache_ptr->resize_ctl.set_initial_size)
922
23
        new_max_cache_size = cache_ptr->resize_ctl.initial_size;
923
0
    else if (cache_ptr->max_cache_size > cache_ptr->resize_ctl.max_size)
924
0
        new_max_cache_size = cache_ptr->resize_ctl.max_size;
925
0
    else if (cache_ptr->max_cache_size < cache_ptr->resize_ctl.min_size)
926
0
        new_max_cache_size = cache_ptr->resize_ctl.min_size;
927
0
    else
928
0
        new_max_cache_size = cache_ptr->max_cache_size;
929
930
23
    new_min_clean_size = (size_t)((double)new_max_cache_size * (cache_ptr->resize_ctl.min_clean_fraction));
931
932
    /* since new_min_clean_size is of type size_t, we have
933
     *
934
     *     ( 0 <= new_min_clean_size )
935
     *
936
     * by definition.
937
     */
938
23
    assert(new_min_clean_size <= new_max_cache_size);
939
23
    assert(cache_ptr->resize_ctl.min_size <= new_max_cache_size);
940
23
    assert(new_max_cache_size <= cache_ptr->resize_ctl.max_size);
941
942
23
    if (new_max_cache_size < cache_ptr->max_cache_size)
943
23
        cache_ptr->size_decreased = true;
944
945
23
    cache_ptr->max_cache_size = new_max_cache_size;
946
23
    cache_ptr->min_clean_size = new_min_clean_size;
947
948
23
    if (H5C_reset_cache_hit_rate_stats(cache_ptr) < 0)
949
        /* this should be impossible... */
950
0
        HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "H5C_reset_cache_hit_rate_stats failed");
951
952
    /* remove excess epoch markers if any */
953
23
    if ((config_ptr->decr_mode == H5C_decr__age_out_with_threshold) ||
954
23
        (config_ptr->decr_mode == H5C_decr__age_out)) {
955
23
        if (cache_ptr->epoch_markers_active > cache_ptr->resize_ctl.epochs_before_eviction)
956
0
            if (H5C__autoadjust__ageout__remove_excess_markers(cache_ptr) < 0)
957
0
                HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "can't remove excess epoch markers");
958
23
    } /* end if */
959
0
    else if (cache_ptr->epoch_markers_active > 0) {
960
0
        if (H5C__autoadjust__ageout__remove_all_markers(cache_ptr) < 0)
961
0
            HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "error removing all epoch markers");
962
0
    }
963
964
    /* configure flash size increase facility.  We wait until the
965
     * end of the function, as we need the max_cache_size set before
966
     * we start to keep things simple.
967
     *
968
     * If we haven't already ruled out flash cache size increases above,
969
     * go ahead and configure it.
970
     */
971
23
    if (cache_ptr->flash_size_increase_possible) {
972
23
        switch (config_ptr->flash_incr_mode) {
973
0
            case H5C_flash_incr__off:
974
0
                cache_ptr->flash_size_increase_possible = false;
975
0
                break;
976
977
23
            case H5C_flash_incr__add_space:
978
23
                cache_ptr->flash_size_increase_possible = true;
979
23
                cache_ptr->flash_size_increase_threshold =
980
23
                    (size_t)(((double)(cache_ptr->max_cache_size)) * (cache_ptr->resize_ctl.flash_threshold));
981
23
                break;
982
983
0
            default: /* should be unreachable */
984
0
                HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Unknown flash_incr_mode?!?!?");
985
0
                break;
986
23
        } /* end switch */
987
23
    }     /* end if */
988
989
23
done:
990
23
    FUNC_LEAVE_NOAPI(ret_value)
991
23
} /* H5C_set_cache_auto_resize_config() */
992
993
/*-------------------------------------------------------------------------
994
 * Function:    H5C_set_evictions_enabled()
995
 *
996
 * Purpose:     Set cache_ptr->evictions_enabled to the value of the
997
 *              evictions enabled parameter.
998
 *
999
 * Return:      SUCCEED on success, and FAIL on failure.
1000
 *
1001
 *-------------------------------------------------------------------------
1002
 */
1003
herr_t
1004
H5C_set_evictions_enabled(H5C_t *cache_ptr, bool evictions_enabled)
1005
23
{
1006
23
    herr_t ret_value = SUCCEED; /* Return value */
1007
1008
23
    FUNC_ENTER_NOAPI(FAIL)
1009
1010
23
    if (cache_ptr == NULL)
1011
0
        HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Bad cache_ptr on entry");
1012
1013
    /* There is no fundamental reason why we should not permit
1014
     * evictions to be disabled while automatic resize is enabled.
1015
     * However, allowing it would greatly complicate testing
1016
     * the feature.  Hence the following:
1017
     */
1018
23
    if ((evictions_enabled != true) && ((cache_ptr->resize_ctl.incr_mode != H5C_incr__off) ||
1019
0
                                        (cache_ptr->resize_ctl.decr_mode != H5C_decr__off)))
1020
0
        HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Can't disable evictions when auto resize enabled");
1021
1022
23
    cache_ptr->evictions_enabled = evictions_enabled;
1023
1024
23
done:
1025
23
    FUNC_LEAVE_NOAPI(ret_value)
1026
23
} /* H5C_set_evictions_enabled() */
1027
1028
/*-------------------------------------------------------------------------
1029
 * Function:    H5C_set_slist_enabled()
1030
 *
1031
 * Purpose:     Enable or disable the slist as directed.
1032
 *
1033
 *              The slist (skip list) is an address ordered list of
1034
 *              dirty entries in the metadata cache.  However, this
1035
 *              list is only needed during flush and close, where we
1036
 *              use it to write entries in more or less increasing
1037
 *              address order.
1038
 *
1039
 *              This function sets up and enables further operations
1040
 *              on the slist, or disable the slist.  This in turn
1041
 *              allows us to avoid the overhead of maintaining the
1042
 *              slist when it is not needed.
1043
 *
1044
 *
1045
 *              If the slist_enabled parameter is true, the function
1046
 *
1047
 *              1) Verifies that the slist is empty.
1048
 *
1049
 *              2) If the populate_slist parameter is true, scans the
1050
 *                 index list, and inserts all dirty entries into the
1051
 *                 slist.
1052
 *
1053
 *              3) Sets cache_ptr->slist_enabled = true.
1054
 *
1055
 *
1056
 *              If the slist_enabled_parameter is false, the function
1057
 *              shuts down the slist:
1058
 *
1059
 *              1) Test to see if the slist is empty.  If it is, proceed
1060
 *                 to step 3.
1061
 *
1062
 *              2) Remove all entries from the slist.
1063
 *
1064
 *              3) set cache_ptr->slist_enabled = false.
1065
 *
1066
 *              Note that the populate_slist parameter is ignored if
1067
 *              the slist_enabed parameter is false.
1068
 *
1069
 * Return:      SUCCEED on success, and FAIL on failure.
1070
 *
1071
 *-------------------------------------------------------------------------
1072
 */
1073
herr_t
1074
H5C_set_slist_enabled(H5C_t *cache_ptr, bool slist_enabled, bool populate_slist)
1075
29
{
1076
29
    H5C_cache_entry_t *entry_ptr;
1077
29
    herr_t             ret_value = SUCCEED; /* Return value */
1078
1079
29
    FUNC_ENTER_NOAPI(FAIL)
1080
1081
29
    if (cache_ptr == NULL)
1082
0
        HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Bad cache_ptr on entry");
1083
1084
29
    if (slist_enabled) {
1085
26
        if (cache_ptr->slist_enabled)
1086
0
            HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "slist already enabled?");
1087
26
        if ((cache_ptr->slist_len != 0) || (cache_ptr->slist_size != 0))
1088
0
            HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "slist not empty?");
1089
1090
        /* set cache_ptr->slist_enabled to true so that the slist
1091
         * maintenance macros will be enabled.
1092
         */
1093
26
        cache_ptr->slist_enabled = true;
1094
1095
26
        if (populate_slist) {
1096
            /* scan the index list and insert all dirty entries in the slist */
1097
24
            entry_ptr = cache_ptr->il_head;
1098
63
            while (entry_ptr != NULL) {
1099
39
                if (entry_ptr->is_dirty)
1100
6
                    H5C__INSERT_ENTRY_IN_SLIST(cache_ptr, entry_ptr, FAIL);
1101
39
                entry_ptr = entry_ptr->il_next;
1102
39
            }
1103
1104
            /* we don't maintain a dirty index len, so we can't do a cross
1105
             * check against it.  Note that there is no point in cross checking
1106
             * against the dirty LRU size, as the dirty LRU may not be maintained,
1107
             * and in any case, there is no requirement that all dirty entries
1108
             * will reside on the dirty LRU.
1109
             */
1110
24
            assert(cache_ptr->dirty_index_size == cache_ptr->slist_size);
1111
24
        }
1112
26
    }
1113
3
    else { /* take down the skip list */
1114
3
        if (!cache_ptr->slist_enabled)
1115
0
            HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "slist already disabled?");
1116
1117
3
        if ((cache_ptr->slist_len != 0) || (cache_ptr->slist_size != 0))
1118
0
            HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "slist not empty?");
1119
1120
3
        cache_ptr->slist_enabled = false;
1121
1122
3
        assert(0 == cache_ptr->slist_len);
1123
3
        assert(0 == cache_ptr->slist_size);
1124
3
    }
1125
1126
29
done:
1127
29
    FUNC_LEAVE_NOAPI(ret_value)
1128
29
} /* H5C_set_slist_enabled() */
1129
1130
/*-------------------------------------------------------------------------
1131
 * Function:    H5C_unsettle_ring()
1132
 *
1133
 * Purpose:     Advise the metadata cache that the specified free space
1134
 *              manager ring is no longer settled (if it was on entry).
1135
 *
1136
 *              If the target free space manager ring is already
1137
 *              unsettled, do nothing, and return SUCCEED.
1138
 *
1139
 *              If the target free space manager ring is settled, and
1140
 *              we are not in the process of a file shutdown, mark
1141
 *              the ring as unsettled, and return SUCCEED.
1142
 *
1143
 *              If the target free space manager is settled, and we
1144
 *              are in the process of a file shutdown, post an error
1145
 *              message, and return FAIL.
1146
 *
1147
 * Return:      Non-negative on success/Negative on failure
1148
 *
1149
 *-------------------------------------------------------------------------
1150
 */
1151
herr_t
1152
H5C_unsettle_ring(H5F_t *f, H5C_ring_t ring)
1153
1
{
1154
1
    H5C_t *cache_ptr;
1155
1
    herr_t ret_value = SUCCEED; /* Return value */
1156
1157
1
    FUNC_ENTER_NOAPI(FAIL)
1158
1159
    /* Sanity checks */
1160
1
    assert(f);
1161
1
    assert(f->shared);
1162
1
    assert(f->shared->cache);
1163
1
    assert((H5C_RING_RDFSM == ring) || (H5C_RING_MDFSM == ring));
1164
1
    cache_ptr = f->shared->cache;
1165
1166
1
    switch (ring) {
1167
0
        case H5C_RING_RDFSM:
1168
0
            if (cache_ptr->rdfsm_settled) {
1169
0
                if (cache_ptr->close_warning_received)
1170
0
                    HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "unexpected rdfsm ring unsettle");
1171
0
                cache_ptr->rdfsm_settled = false;
1172
0
            } /* end if */
1173
0
            break;
1174
1175
1
        case H5C_RING_MDFSM:
1176
1
            if (cache_ptr->mdfsm_settled) {
1177
0
                if (cache_ptr->close_warning_received)
1178
0
                    HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "unexpected mdfsm ring unsettle");
1179
0
                cache_ptr->mdfsm_settled = false;
1180
0
            } /* end if */
1181
1
            break;
1182
1183
1
        default:
1184
0
            assert(false); /* this should be un-reachable */
1185
0
            break;
1186
1
    } /* end switch */
1187
1188
1
done:
1189
1
    FUNC_LEAVE_NOAPI(ret_value)
1190
1
} /* H5C_unsettle_ring() */
1191
1192
/*-------------------------------------------------------------------------
1193
 * Function:    H5C_validate_resize_config()
1194
 *
1195
 * Purpose:    Run a sanity check on the specified sections of the
1196
 *             provided instance of struct H5C_auto_size_ctl_t.
1197
 *
1198
 *        Do nothing and return SUCCEED if no errors are detected,
1199
 *        and flag an error and return FAIL otherwise.
1200
 *
1201
 * Return:      Non-negative on success/Negative on failure
1202
 *
1203
 *-------------------------------------------------------------------------
1204
 */
1205
herr_t
1206
H5C_validate_resize_config(H5C_auto_size_ctl_t *config_ptr, unsigned int tests)
1207
138
{
1208
138
    herr_t ret_value = SUCCEED; /* Return value */
1209
1210
138
    FUNC_ENTER_NOAPI(FAIL)
1211
1212
138
    if (config_ptr == NULL)
1213
0
        HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "NULL config_ptr on entry");
1214
1215
138
    if (config_ptr->version != H5C__CURR_AUTO_SIZE_CTL_VER)
1216
0
        HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Unknown config version");
1217
1218
138
    if ((tests & H5C_RESIZE_CFG__VALIDATE_GENERAL) != 0) {
1219
69
        if (config_ptr->max_size > H5C__MAX_MAX_CACHE_SIZE)
1220
0
            HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "max_size too big");
1221
69
        if (config_ptr->min_size < H5C__MIN_MAX_CACHE_SIZE)
1222
0
            HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "min_size too small");
1223
69
        if (config_ptr->min_size > config_ptr->max_size)
1224
0
            HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "min_size > max_size");
1225
69
        if (config_ptr->set_initial_size && ((config_ptr->initial_size < config_ptr->min_size) ||
1226
69
                                             (config_ptr->initial_size > config_ptr->max_size)))
1227
0
            HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL,
1228
69
                        "initial_size must be in the interval [min_size, max_size]");
1229
69
        if ((config_ptr->min_clean_fraction < 0.0) || (config_ptr->min_clean_fraction > 1.0))
1230
0
            HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL,
1231
69
                        "min_clean_fraction must be in the interval [0.0, 1.0]");
1232
69
        if (config_ptr->epoch_length < H5C__MIN_AR_EPOCH_LENGTH)
1233
0
            HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "epoch_length too small");
1234
69
        if (config_ptr->epoch_length > H5C__MAX_AR_EPOCH_LENGTH)
1235
0
            HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "epoch_length too big");
1236
69
    } /* H5C_RESIZE_CFG__VALIDATE_GENERAL */
1237
1238
138
    if ((tests & H5C_RESIZE_CFG__VALIDATE_INCREMENT) != 0) {
1239
69
        if ((config_ptr->incr_mode != H5C_incr__off) && (config_ptr->incr_mode != H5C_incr__threshold))
1240
0
            HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "Invalid incr_mode");
1241
1242
69
        if (config_ptr->incr_mode == H5C_incr__threshold) {
1243
69
            if ((config_ptr->lower_hr_threshold < 0.0) || (config_ptr->lower_hr_threshold > 1.0))
1244
0
                HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL,
1245
69
                            "lower_hr_threshold must be in the range [0.0, 1.0]");
1246
69
            if (config_ptr->increment < 1.0)
1247
0
                HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "increment must be greater than or equal to 1.0");
1248
1249
            /* no need to check max_increment, as it is a size_t,
1250
             * and thus must be non-negative.
1251
             */
1252
69
        } /* H5C_incr__threshold */
1253
1254
69
        switch (config_ptr->flash_incr_mode) {
1255
0
            case H5C_flash_incr__off:
1256
                /* nothing to do here */
1257
0
                break;
1258
1259
69
            case H5C_flash_incr__add_space:
1260
69
                if ((config_ptr->flash_multiple < 0.1) || (config_ptr->flash_multiple > 10.0))
1261
0
                    HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL,
1262
69
                                "flash_multiple must be in the range [0.1, 10.0]");
1263
69
                if ((config_ptr->flash_threshold < 0.1) || (config_ptr->flash_threshold > 1.0))
1264
0
                    HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL,
1265
69
                                "flash_threshold must be in the range [0.1, 1.0]");
1266
69
                break;
1267
1268
69
            default:
1269
0
                HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "Invalid flash_incr_mode");
1270
0
                break;
1271
69
        } /* end switch */
1272
69
    }     /* H5C_RESIZE_CFG__VALIDATE_INCREMENT */
1273
1274
138
    if ((tests & H5C_RESIZE_CFG__VALIDATE_DECREMENT) != 0) {
1275
69
        if ((config_ptr->decr_mode != H5C_decr__off) && (config_ptr->decr_mode != H5C_decr__threshold) &&
1276
69
            (config_ptr->decr_mode != H5C_decr__age_out) &&
1277
69
            (config_ptr->decr_mode != H5C_decr__age_out_with_threshold))
1278
0
            HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "Invalid decr_mode");
1279
1280
69
        if (config_ptr->decr_mode == H5C_decr__threshold) {
1281
0
            if (config_ptr->upper_hr_threshold > 1.0)
1282
0
                HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "upper_hr_threshold must be <= 1.0");
1283
0
            if ((config_ptr->decrement > 1.0) || (config_ptr->decrement < 0.0))
1284
0
                HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "decrement must be in the interval [0.0, 1.0]");
1285
1286
            /* no need to check max_decrement as it is a size_t
1287
             * and thus must be non-negative.
1288
             */
1289
0
        } /* H5C_decr__threshold */
1290
1291
69
        if ((config_ptr->decr_mode == H5C_decr__age_out) ||
1292
69
            (config_ptr->decr_mode == H5C_decr__age_out_with_threshold)) {
1293
69
            if (config_ptr->epochs_before_eviction < 1)
1294
0
                HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "epochs_before_eviction must be positive");
1295
69
            if (config_ptr->epochs_before_eviction > H5C__MAX_EPOCH_MARKERS)
1296
0
                HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "epochs_before_eviction too big");
1297
69
            if (config_ptr->apply_empty_reserve &&
1298
69
                (config_ptr->empty_reserve > 1.0 || config_ptr->empty_reserve < 0.0))
1299
0
                HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "empty_reserve must be in the interval [0.0, 1.0]");
1300
1301
            /* no need to check max_decrement as it is a size_t
1302
             * and thus must be non-negative.
1303
             */
1304
69
        } /* H5C_decr__age_out || H5C_decr__age_out_with_threshold */
1305
1306
69
        if (config_ptr->decr_mode == H5C_decr__age_out_with_threshold)
1307
69
            if ((config_ptr->upper_hr_threshold > 1.0) || (config_ptr->upper_hr_threshold < 0.0))
1308
0
                HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL,
1309
69
                            "upper_hr_threshold must be in the interval [0.0, 1.0]");
1310
69
    } /* H5C_RESIZE_CFG__VALIDATE_DECREMENT */
1311
1312
138
    if ((tests & H5C_RESIZE_CFG__VALIDATE_INTERACTIONS) != 0) {
1313
69
        if ((config_ptr->incr_mode == H5C_incr__threshold) &&
1314
69
            ((config_ptr->decr_mode == H5C_decr__threshold) ||
1315
69
             (config_ptr->decr_mode == H5C_decr__age_out_with_threshold)) &&
1316
69
            (config_ptr->lower_hr_threshold >= config_ptr->upper_hr_threshold))
1317
0
            HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "conflicting threshold fields in config");
1318
69
    } /* H5C_RESIZE_CFG__VALIDATE_INTERACTIONS */
1319
1320
138
done:
1321
138
    FUNC_LEAVE_NOAPI(ret_value)
1322
138
} /* H5C_validate_resize_config() */
1323
1324
/*-------------------------------------------------------------------------
1325
 * Function:    H5C_cork
1326
 *
1327
 * Purpose:     To cork/uncork/get cork status of an object depending on "action":
1328
 *        H5C__SET_CORK:
1329
 *            To cork the object
1330
 *            Return error if the object is already corked
1331
 *        H5C__UNCORK:
1332
 *            To uncork the object
1333
 *            Return error if the object is not corked
1334
 *         H5C__GET_CORKED:
1335
 *            To retrieve the cork status of an object in
1336
 *            the parameter "corked"
1337
 *
1338
 * Return:      Success:        Non-negative
1339
 *              Failure:        Negative
1340
 *
1341
 *-------------------------------------------------------------------------
1342
 */
1343
herr_t
1344
H5C_cork(H5C_t *cache_ptr, haddr_t obj_addr, unsigned action, bool *corked)
1345
0
{
1346
0
    H5C_tag_info_t *tag_info  = NULL;
1347
0
    herr_t          ret_value = SUCCEED;
1348
1349
0
    FUNC_ENTER_NOAPI_NOINIT
1350
1351
    /* Assertions */
1352
0
    assert(cache_ptr != NULL);
1353
0
    assert(H5_addr_defined(obj_addr));
1354
0
    assert(action == H5C__SET_CORK || action == H5C__UNCORK || action == H5C__GET_CORKED);
1355
1356
    /* Search the list of corked object addresses in the cache */
1357
0
    HASH_FIND(hh, cache_ptr->tag_list, &obj_addr, sizeof(haddr_t), tag_info);
1358
1359
0
    if (H5C__GET_CORKED == action) {
1360
0
        assert(corked);
1361
0
        if (tag_info != NULL && tag_info->corked)
1362
0
            *corked = true;
1363
0
        else
1364
0
            *corked = false;
1365
0
    }
1366
0
    else {
1367
        /* Sanity check */
1368
0
        assert(H5C__SET_CORK == action || H5C__UNCORK == action);
1369
1370
        /* Perform appropriate action */
1371
0
        if (H5C__SET_CORK == action) {
1372
            /* Check if this is the first entry for this tagged object */
1373
0
            if (NULL == tag_info) {
1374
                /* Allocate new tag info struct */
1375
0
                if (NULL == (tag_info = H5FL_CALLOC(H5C_tag_info_t)))
1376
0
                    HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, FAIL, "can't allocate tag info for cache entry");
1377
1378
                /* Set the tag for all entries */
1379
0
                tag_info->tag = obj_addr;
1380
1381
                /* Insert tag info into hash table */
1382
0
                HASH_ADD(hh, cache_ptr->tag_list, tag, sizeof(haddr_t), tag_info);
1383
0
            }
1384
0
            else {
1385
                /* Check for object already corked */
1386
0
                if (tag_info->corked)
1387
0
                    HGOTO_ERROR(H5E_CACHE, H5E_CANTCORK, FAIL, "object already corked");
1388
0
                assert(tag_info->entry_cnt > 0 && tag_info->head);
1389
0
            }
1390
1391
            /* Set the corked status for the entire object */
1392
0
            tag_info->corked = true;
1393
0
            cache_ptr->num_objs_corked++;
1394
0
        }
1395
0
        else {
1396
            /* Sanity check */
1397
0
            if (NULL == tag_info)
1398
0
                HGOTO_ERROR(H5E_CACHE, H5E_CANTUNCORK, FAIL, "tag info pointer is NULL");
1399
1400
            /* Check for already uncorked */
1401
0
            if (!tag_info->corked)
1402
0
                HGOTO_ERROR(H5E_CACHE, H5E_CANTUNCORK, FAIL, "object already uncorked");
1403
1404
            /* Set the corked status for the entire object */
1405
0
            tag_info->corked = false;
1406
0
            cache_ptr->num_objs_corked--;
1407
1408
            /* Remove the tag info from the tag list, if there's no more entries with this tag */
1409
0
            if (0 == tag_info->entry_cnt) {
1410
                /* Sanity check */
1411
0
                assert(NULL == tag_info->head);
1412
1413
0
                HASH_DELETE(hh, cache_ptr->tag_list, tag_info);
1414
1415
                /* Release the tag info */
1416
0
                tag_info = H5FL_FREE(H5C_tag_info_t, tag_info);
1417
0
            }
1418
0
            else
1419
0
                assert(NULL != tag_info->head);
1420
0
        }
1421
0
    }
1422
1423
0
done:
1424
0
    FUNC_LEAVE_NOAPI(ret_value)
1425
0
} /* H5C_cork() */