Coverage Report

Created: 2026-08-31 06:23

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/hdf5/src/H5Cint.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:     H5Centry.c
16
 *
17
 * Purpose:     Routines which operate on cache entries.
18
 *
19
 *-------------------------------------------------------------------------
20
 */
21
22
/****************/
23
/* Module Setup */
24
/****************/
25
26
#include "H5Cmodule.h" /* This source code file is part of the H5C module */
27
#define H5F_FRIEND     /* suppress error about including H5Fpkg  */
28
29
/***********/
30
/* Headers */
31
/***********/
32
#include "H5private.h"   /* Generic Functions            */
33
#include "H5Cpkg.h"      /* Cache                        */
34
#include "H5Eprivate.h"  /* Error handling               */
35
#include "H5Fpkg.h"      /* Files                        */
36
#include "H5MFprivate.h" /* File memory management       */
37
#include "H5SLprivate.h" /* Skip Lists                               */
38
39
/****************/
40
/* Local Macros */
41
/****************/
42
43
/******************/
44
/* Local Typedefs */
45
/******************/
46
47
/********************/
48
/* Local Prototypes */
49
/********************/
50
static herr_t H5C__autoadjust__ageout(H5F_t *f, double hit_rate, enum H5C_resize_status *status_ptr,
51
                                      size_t *new_max_cache_size_ptr, bool write_permitted);
52
static herr_t H5C__autoadjust__ageout__cycle_epoch_marker(H5C_t *cache_ptr);
53
static herr_t H5C__autoadjust__ageout__evict_aged_out_entries(H5F_t *f, bool write_permitted);
54
static herr_t H5C__autoadjust__ageout__insert_new_marker(H5C_t *cache_ptr);
55
static herr_t H5C__flush_invalidate_ring(H5F_t *f, H5C_ring_t ring, unsigned flags);
56
static herr_t H5C__serialize_ring(H5F_t *f, H5C_ring_t ring);
57
58
/*********************/
59
/* Package Variables */
60
/*********************/
61
62
/*****************************/
63
/* Library Private Variables */
64
/*****************************/
65
66
/*******************/
67
/* Local Variables */
68
/*******************/
69
70
/*-------------------------------------------------------------------------
71
 * Function:    H5C__auto_adjust_cache_size
72
 *
73
 * Purpose:        Obtain the current full cache hit rate, and compare it
74
 *        with the hit rate thresholds for modifying cache size.
75
 *        If one of the thresholds has been crossed, adjusts the
76
 *        size of the cache accordingly.
77
 *
78
 *        The function then resets the full cache hit rate
79
 *        statistics, and exits.
80
 *
81
 * Return:      Non-negative on success/Negative on failure or if there was
82
 *        an attempt to flush a protected item.
83
 *
84
 *-------------------------------------------------------------------------
85
 */
86
herr_t
87
H5C__auto_adjust_cache_size(H5F_t *f, bool write_permitted)
88
0
{
89
0
    H5C_t                 *cache_ptr             = f->shared->cache;
90
0
    bool                   reentrant_call        = false;
91
0
    bool                   inserted_epoch_marker = false;
92
0
    size_t                 new_max_cache_size    = 0;
93
0
    size_t                 old_max_cache_size    = 0;
94
0
    size_t                 new_min_clean_size    = 0;
95
0
    size_t                 old_min_clean_size    = 0;
96
0
    double                 hit_rate;
97
0
    enum H5C_resize_status status    = in_spec; /* will change if needed */
98
0
    herr_t                 ret_value = SUCCEED; /* Return value */
99
100
0
    FUNC_ENTER_PACKAGE
101
102
0
    assert(f);
103
0
    assert(cache_ptr);
104
0
    assert(cache_ptr->cache_accesses >= cache_ptr->resize_ctl.epoch_length);
105
0
    assert(0.0 <= cache_ptr->resize_ctl.min_clean_fraction);
106
0
    assert(cache_ptr->resize_ctl.min_clean_fraction <= 100.0);
107
108
    /* check to see if cache_ptr->resize_in_progress is true.  If it, this
109
     * is a re-entrant call via a client callback called in the resize
110
     * process.  To avoid an infinite recursion, set reentrant_call to
111
     * true, and goto done.
112
     */
113
0
    if (cache_ptr->resize_in_progress) {
114
0
        reentrant_call = true;
115
0
        HGOTO_DONE(SUCCEED);
116
0
    } /* end if */
117
118
0
    cache_ptr->resize_in_progress = true;
119
120
0
    if (!cache_ptr->resize_enabled)
121
0
        HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Auto cache resize disabled");
122
123
0
    assert((cache_ptr->resize_ctl.incr_mode != H5C_incr__off) ||
124
0
           (cache_ptr->resize_ctl.decr_mode != H5C_decr__off));
125
126
0
    if (H5C_get_cache_hit_rate(cache_ptr, &hit_rate) != SUCCEED)
127
0
        HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Can't get hit rate");
128
129
0
    assert((0.0 <= hit_rate) && (hit_rate <= 1.0));
130
131
0
    switch (cache_ptr->resize_ctl.incr_mode) {
132
0
        case H5C_incr__off:
133
0
            if (cache_ptr->size_increase_possible)
134
0
                HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "size_increase_possible but H5C_incr__off?!?!?");
135
0
            break;
136
137
0
        case H5C_incr__threshold:
138
0
            if (hit_rate < cache_ptr->resize_ctl.lower_hr_threshold) {
139
0
                if (!cache_ptr->size_increase_possible)
140
0
                    status = increase_disabled;
141
0
                else if (cache_ptr->max_cache_size >= cache_ptr->resize_ctl.max_size) {
142
0
                    assert(cache_ptr->max_cache_size == cache_ptr->resize_ctl.max_size);
143
0
                    status = at_max_size;
144
0
                }
145
0
                else if (!cache_ptr->cache_full)
146
0
                    status = not_full;
147
0
                else {
148
0
                    new_max_cache_size =
149
0
                        (size_t)(((double)(cache_ptr->max_cache_size)) * cache_ptr->resize_ctl.increment);
150
151
                    /* clip to max size if necessary */
152
0
                    if (new_max_cache_size > cache_ptr->resize_ctl.max_size)
153
0
                        new_max_cache_size = cache_ptr->resize_ctl.max_size;
154
155
                    /* clip to max increment if necessary */
156
0
                    if (cache_ptr->resize_ctl.apply_max_increment &&
157
0
                        ((cache_ptr->max_cache_size + cache_ptr->resize_ctl.max_increment) <
158
0
                         new_max_cache_size))
159
0
                        new_max_cache_size = cache_ptr->max_cache_size + cache_ptr->resize_ctl.max_increment;
160
161
0
                    status = increase;
162
0
                }
163
0
            }
164
0
            break;
165
166
0
        default:
167
0
            HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "unknown incr_mode");
168
0
    }
169
170
    /* If the decr_mode is either age out or age out with threshold, we
171
     * must run the marker maintenance code, whether we run the size
172
     * reduction code or not.  We do this in two places -- here we
173
     * insert a new marker if the number of active epoch markers is
174
     * is less than the current epochs before eviction, and after
175
     * the ageout call, we cycle the markers.
176
     *
177
     * However, we can't call the ageout code or cycle the markers
178
     * unless there was a full complement of markers in place on
179
     * entry.  The inserted_epoch_marker flag is used to track this.
180
     */
181
182
0
    if (((cache_ptr->resize_ctl.decr_mode == H5C_decr__age_out) ||
183
0
         (cache_ptr->resize_ctl.decr_mode == H5C_decr__age_out_with_threshold)) &&
184
0
        (cache_ptr->epoch_markers_active < cache_ptr->resize_ctl.epochs_before_eviction)) {
185
186
0
        if (H5C__autoadjust__ageout__insert_new_marker(cache_ptr) < 0)
187
0
            HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "can't insert new epoch marker");
188
189
0
        inserted_epoch_marker = true;
190
0
    }
191
192
    /* don't run the cache size decrease code unless the cache size
193
     * increase code is disabled, or the size increase code sees no need
194
     * for action.  In either case, status == in_spec at this point.
195
     */
196
197
0
    if (status == in_spec) {
198
0
        switch (cache_ptr->resize_ctl.decr_mode) {
199
0
            case H5C_decr__off:
200
0
                break;
201
202
0
            case H5C_decr__threshold:
203
0
                if (hit_rate > cache_ptr->resize_ctl.upper_hr_threshold) {
204
0
                    if (!cache_ptr->size_decrease_possible)
205
0
                        status = decrease_disabled;
206
0
                    else if (cache_ptr->max_cache_size <= cache_ptr->resize_ctl.min_size) {
207
0
                        assert(cache_ptr->max_cache_size == cache_ptr->resize_ctl.min_size);
208
0
                        status = at_min_size;
209
0
                    }
210
0
                    else {
211
0
                        new_max_cache_size =
212
0
                            (size_t)(((double)(cache_ptr->max_cache_size)) * cache_ptr->resize_ctl.decrement);
213
214
                        /* clip to min size if necessary */
215
0
                        if (new_max_cache_size < cache_ptr->resize_ctl.min_size)
216
0
                            new_max_cache_size = cache_ptr->resize_ctl.min_size;
217
218
                        /* clip to max decrement if necessary */
219
0
                        if (cache_ptr->resize_ctl.apply_max_decrement &&
220
0
                            ((cache_ptr->resize_ctl.max_decrement + new_max_cache_size) <
221
0
                             cache_ptr->max_cache_size))
222
0
                            new_max_cache_size =
223
0
                                cache_ptr->max_cache_size - cache_ptr->resize_ctl.max_decrement;
224
225
0
                        status = decrease;
226
0
                    }
227
0
                }
228
0
                break;
229
230
0
            case H5C_decr__age_out_with_threshold:
231
0
            case H5C_decr__age_out:
232
0
                if (!inserted_epoch_marker) {
233
0
                    if (!cache_ptr->size_decrease_possible)
234
0
                        status = decrease_disabled;
235
0
                    else {
236
0
                        if (H5C__autoadjust__ageout(f, hit_rate, &status, &new_max_cache_size,
237
0
                                                    write_permitted) < 0)
238
0
                            HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "ageout code failed");
239
0
                    } /* end else */
240
0
                }     /* end if */
241
0
                break;
242
243
0
            default:
244
0
                HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "unknown incr_mode");
245
0
        }
246
0
    }
247
248
    /* cycle the epoch markers here if appropriate */
249
0
    if (((cache_ptr->resize_ctl.decr_mode == H5C_decr__age_out) ||
250
0
         (cache_ptr->resize_ctl.decr_mode == H5C_decr__age_out_with_threshold)) &&
251
0
        !inserted_epoch_marker)
252
        /* move last epoch marker to the head of the LRU list */
253
0
        if (H5C__autoadjust__ageout__cycle_epoch_marker(cache_ptr) < 0)
254
0
            HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "error cycling epoch marker");
255
256
0
    if ((status == increase) || (status == decrease)) {
257
0
        old_max_cache_size = cache_ptr->max_cache_size;
258
0
        old_min_clean_size = cache_ptr->min_clean_size;
259
260
0
        new_min_clean_size =
261
0
            (size_t)((double)new_max_cache_size * (cache_ptr->resize_ctl.min_clean_fraction));
262
263
        /* new_min_clean_size is of size_t, and thus must be non-negative.
264
         * Hence we have
265
         *
266
         *     ( 0 <= new_min_clean_size ).
267
         *
268
         * by definition.
269
         */
270
0
        assert(new_min_clean_size <= new_max_cache_size);
271
0
        assert(cache_ptr->resize_ctl.min_size <= new_max_cache_size);
272
0
        assert(new_max_cache_size <= cache_ptr->resize_ctl.max_size);
273
274
0
        cache_ptr->max_cache_size = new_max_cache_size;
275
0
        cache_ptr->min_clean_size = new_min_clean_size;
276
277
0
        if (status == increase)
278
0
            cache_ptr->cache_full = false;
279
0
        else if (status == decrease)
280
0
            cache_ptr->size_decreased = true;
281
282
        /* update flash cache size increase fields as appropriate */
283
0
        if (cache_ptr->flash_size_increase_possible) {
284
0
            switch (cache_ptr->resize_ctl.flash_incr_mode) {
285
0
                case H5C_flash_incr__off:
286
0
                    HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL,
287
0
                                "flash_size_increase_possible but H5C_flash_incr__off?!");
288
0
                    break;
289
290
0
                case H5C_flash_incr__add_space:
291
0
                    cache_ptr->flash_size_increase_threshold =
292
0
                        (size_t)(((double)(cache_ptr->max_cache_size)) *
293
0
                                 (cache_ptr->resize_ctl.flash_threshold));
294
0
                    break;
295
296
0
                default: /* should be unreachable */
297
0
                    HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Unknown flash_incr_mode?!?!?");
298
0
                    break;
299
0
            }
300
0
        }
301
0
    }
302
303
0
    if (cache_ptr->resize_ctl.rpt_fcn != NULL)
304
0
        (cache_ptr->resize_ctl.rpt_fcn)(cache_ptr, H5C__CURR_AUTO_RESIZE_RPT_FCN_VER, hit_rate, status,
305
0
                                        old_max_cache_size, new_max_cache_size, old_min_clean_size,
306
0
                                        new_min_clean_size);
307
308
0
    if (H5C_reset_cache_hit_rate_stats(cache_ptr) < 0)
309
        /* this should be impossible... */
310
0
        HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "H5C_reset_cache_hit_rate_stats failed");
311
312
0
done:
313
    /* Sanity checks */
314
0
    assert(cache_ptr->resize_in_progress);
315
0
    if (!reentrant_call)
316
0
        cache_ptr->resize_in_progress = false;
317
0
    assert((!reentrant_call) || (cache_ptr->resize_in_progress));
318
319
0
    FUNC_LEAVE_NOAPI(ret_value)
320
0
} /* H5C__auto_adjust_cache_size() */
321
322
/*-------------------------------------------------------------------------
323
 * Function:    H5C__autoadjust__ageout
324
 *
325
 * Purpose:     Implement the ageout automatic cache size decrement
326
 *        algorithm.  Note that while this code evicts aged out
327
 *        entries, the code does not change the maximum cache size.
328
 *        Instead, the function simply computes the new value (if
329
 *        any change is indicated) and reports this value in
330
 *        *new_max_cache_size_ptr.
331
 *
332
 * Return:      Non-negative on success/Negative on failure or if there was
333
 *              an attempt to flush a protected item.
334
 *
335
 *-------------------------------------------------------------------------
336
 */
337
static herr_t
338
H5C__autoadjust__ageout(H5F_t *f, double hit_rate, enum H5C_resize_status *status_ptr,
339
                        size_t *new_max_cache_size_ptr, bool write_permitted)
340
0
{
341
0
    H5C_t *cache_ptr = f->shared->cache;
342
0
    size_t test_size;
343
0
    herr_t ret_value = SUCCEED; /* Return value */
344
345
0
    FUNC_ENTER_PACKAGE
346
347
0
    assert(f);
348
0
    assert(cache_ptr);
349
0
    assert((status_ptr) && (*status_ptr == in_spec));
350
0
    assert((new_max_cache_size_ptr) && (*new_max_cache_size_ptr == 0));
351
352
    /* remove excess epoch markers if any */
353
0
    if (cache_ptr->epoch_markers_active > cache_ptr->resize_ctl.epochs_before_eviction)
354
0
        if (H5C__autoadjust__ageout__remove_excess_markers(cache_ptr) < 0)
355
0
            HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "can't remove excess epoch markers");
356
357
0
    if ((cache_ptr->resize_ctl.decr_mode == H5C_decr__age_out) ||
358
0
        ((cache_ptr->resize_ctl.decr_mode == H5C_decr__age_out_with_threshold) &&
359
0
         (hit_rate >= cache_ptr->resize_ctl.upper_hr_threshold))) {
360
361
0
        if (cache_ptr->max_cache_size > cache_ptr->resize_ctl.min_size) {
362
            /* evict aged out cache entries if appropriate... */
363
0
            if (H5C__autoadjust__ageout__evict_aged_out_entries(f, write_permitted) < 0)
364
0
                HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "error flushing aged out entries");
365
366
            /* ... and then reduce cache size if appropriate */
367
0
            if (cache_ptr->index_size < cache_ptr->max_cache_size) {
368
0
                if (cache_ptr->resize_ctl.apply_empty_reserve) {
369
0
                    test_size =
370
0
                        (size_t)(((double)cache_ptr->index_size) / (1 - cache_ptr->resize_ctl.empty_reserve));
371
0
                    if (test_size < cache_ptr->max_cache_size) {
372
0
                        *status_ptr             = decrease;
373
0
                        *new_max_cache_size_ptr = test_size;
374
0
                    }
375
0
                }
376
0
                else {
377
0
                    *status_ptr             = decrease;
378
0
                    *new_max_cache_size_ptr = cache_ptr->index_size;
379
0
                }
380
381
0
                if (*status_ptr == decrease) {
382
                    /* clip to min size if necessary */
383
0
                    if (*new_max_cache_size_ptr < cache_ptr->resize_ctl.min_size)
384
0
                        *new_max_cache_size_ptr = cache_ptr->resize_ctl.min_size;
385
386
                    /* clip to max decrement if necessary */
387
0
                    if ((cache_ptr->resize_ctl.apply_max_decrement) &&
388
0
                        ((cache_ptr->resize_ctl.max_decrement + *new_max_cache_size_ptr) <
389
0
                         cache_ptr->max_cache_size))
390
0
                        *new_max_cache_size_ptr =
391
0
                            cache_ptr->max_cache_size - cache_ptr->resize_ctl.max_decrement;
392
0
                }
393
0
            }
394
0
        }
395
0
        else
396
0
            *status_ptr = at_min_size;
397
0
    }
398
399
0
done:
400
0
    FUNC_LEAVE_NOAPI(ret_value)
401
0
} /* H5C__autoadjust__ageout() */
402
403
/*-------------------------------------------------------------------------
404
 * Function:    H5C__autoadjust__ageout__cycle_epoch_marker
405
 *
406
 * Purpose:     Remove the oldest epoch marker from the LRU list,
407
 *        and reinsert it at the head of the LRU list.  Also
408
 *        remove the epoch marker's index from the head of the
409
 *        ring buffer, and re-insert it at the tail of the ring
410
 *        buffer.
411
 *
412
 * Return:      SUCCEED on success/FAIL on failure.
413
 *
414
 *-------------------------------------------------------------------------
415
 */
416
static herr_t
417
H5C__autoadjust__ageout__cycle_epoch_marker(H5C_t *cache_ptr)
418
0
{
419
0
    int    i;
420
0
    herr_t ret_value = SUCCEED; /* Return value */
421
422
0
    FUNC_ENTER_PACKAGE
423
424
0
    assert(cache_ptr);
425
426
0
    if (cache_ptr->epoch_markers_active <= 0)
427
0
        HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "No active epoch markers on entry?!?!?");
428
429
    /* remove the last marker from both the ring buffer and the LRU list */
430
0
    i = cache_ptr->epoch_marker_ringbuf[cache_ptr->epoch_marker_ringbuf_first];
431
0
    cache_ptr->epoch_marker_ringbuf_first =
432
0
        (cache_ptr->epoch_marker_ringbuf_first + 1) % (H5C__MAX_EPOCH_MARKERS + 1);
433
0
    if (cache_ptr->epoch_marker_ringbuf_size <= 0)
434
0
        HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "ring buffer underflow");
435
436
0
    cache_ptr->epoch_marker_ringbuf_size -= 1;
437
0
    if (cache_ptr->epoch_marker_active[i] != true)
438
0
        HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "unused marker in LRU?!?");
439
440
0
    H5C__DLL_REMOVE((&((cache_ptr->epoch_markers)[i])), (cache_ptr)->LRU_head_ptr, (cache_ptr)->LRU_tail_ptr,
441
0
                    (cache_ptr)->LRU_list_len, (cache_ptr)->LRU_list_size, (FAIL))
442
443
    /* now, re-insert it at the head of the LRU list, and at the tail of
444
     * the ring buffer.
445
     */
446
0
    assert(cache_ptr->epoch_markers[i].addr == (haddr_t)i);
447
0
    assert(cache_ptr->epoch_markers[i].next == NULL);
448
0
    assert(cache_ptr->epoch_markers[i].prev == NULL);
449
450
0
    cache_ptr->epoch_marker_ringbuf_last =
451
0
        (cache_ptr->epoch_marker_ringbuf_last + 1) % (H5C__MAX_EPOCH_MARKERS + 1);
452
0
    cache_ptr->epoch_marker_ringbuf[cache_ptr->epoch_marker_ringbuf_last] = i;
453
0
    if (cache_ptr->epoch_marker_ringbuf_size >= H5C__MAX_EPOCH_MARKERS)
454
0
        HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "ring buffer overflow");
455
456
0
    cache_ptr->epoch_marker_ringbuf_size += 1;
457
458
0
    H5C__DLL_PREPEND(&(cache_ptr->epoch_markers[i]), cache_ptr->LRU_head_ptr, cache_ptr->LRU_tail_ptr,
459
0
                     cache_ptr->LRU_list_len, cache_ptr->LRU_list_size, FAIL)
460
0
done:
461
462
0
    FUNC_LEAVE_NOAPI(ret_value)
463
464
0
} /* H5C__autoadjust__ageout__cycle_epoch_marker() */
465
466
/*-------------------------------------------------------------------------
467
 * Function:    H5C__autoadjust__ageout__evict_aged_out_entries
468
 *
469
 * Purpose:     Evict clean entries in the cache that haven't
470
 *        been accessed for at least
471
 *        cache_ptr->resize_ctl.epochs_before_eviction epochs,
472
 *        and flush dirty entries that haven't been accessed for
473
 *        that amount of time.
474
 *
475
 *        Depending on configuration, the function will either
476
 *        flush or evict all such entries, or all such entries it
477
 *        encounters until it has freed the maximum amount of space
478
 *        allowed under the maximum decrement.
479
 *
480
 *        If we are running in parallel mode, writes may not be
481
 *        permitted.  If so, the function simply skips any dirty
482
 *        entries it may encounter.
483
 *
484
 *        The function makes no attempt to maintain the minimum
485
 *        clean size, as there is no guarantee that the cache size
486
 *        will be changed.
487
 *
488
 *        If there is no cache size change, the minimum clean size
489
 *        constraint will be met through a combination of clean
490
 *        entries and free space in the cache.
491
 *
492
 *        If there is a cache size reduction, the minimum clean size
493
 *        will be re-calculated, and will be enforced the next time
494
 *        we have to make space in the cache.
495
 *
496
 * Return:      Non-negative on success/Negative on failure.
497
 *
498
 *-------------------------------------------------------------------------
499
 */
500
static herr_t
501
H5C__autoadjust__ageout__evict_aged_out_entries(H5F_t *f, bool write_permitted)
502
0
{
503
0
    H5C_t             *cache_ptr = f->shared->cache;
504
0
    size_t             eviction_size_limit;
505
0
    size_t             bytes_evicted = 0;
506
0
    bool               prev_is_dirty = false;
507
0
    bool               restart_scan;
508
0
    H5C_cache_entry_t *entry_ptr;
509
0
    H5C_cache_entry_t *next_ptr;
510
0
    H5C_cache_entry_t *prev_ptr;
511
0
    herr_t             ret_value = SUCCEED; /* Return value */
512
513
0
    FUNC_ENTER_PACKAGE
514
515
0
    assert(f);
516
0
    assert(cache_ptr);
517
518
    /* if there is a limit on the amount that the cache size can be decrease
519
     * in any one round of the cache size reduction algorithm, load that
520
     * limit into eviction_size_limit.  Otherwise, set eviction_size_limit
521
     * to the equivalent of infinity.  The current size of the index will
522
     * do nicely.
523
     */
524
0
    if (cache_ptr->resize_ctl.apply_max_decrement)
525
0
        eviction_size_limit = cache_ptr->resize_ctl.max_decrement;
526
0
    else
527
0
        eviction_size_limit = cache_ptr->index_size; /* i.e. infinity */
528
529
0
    if (write_permitted) {
530
0
        restart_scan = false;
531
0
        entry_ptr    = cache_ptr->LRU_tail_ptr;
532
0
        while (entry_ptr != NULL && entry_ptr->type->id != H5AC_EPOCH_MARKER_ID &&
533
0
               bytes_evicted < eviction_size_limit) {
534
0
            bool skipping_entry = false;
535
536
0
            assert(!(entry_ptr->is_protected));
537
0
            assert(!(entry_ptr->is_read_only));
538
0
            assert((entry_ptr->ro_ref_count) == 0);
539
540
0
            next_ptr = entry_ptr->next;
541
0
            prev_ptr = entry_ptr->prev;
542
543
0
            if (prev_ptr != NULL)
544
0
                prev_is_dirty = prev_ptr->is_dirty;
545
546
0
            if (entry_ptr->is_dirty) {
547
0
                assert(!entry_ptr->prefetched_dirty);
548
549
                /* dirty corked entry is skipped */
550
0
                if (entry_ptr->tag_info && entry_ptr->tag_info->corked)
551
0
                    skipping_entry = true;
552
0
                else {
553
                    /* reset entries_removed_counter and
554
                     * last_entry_removed_ptr prior to the call to
555
                     * H5C__flush_single_entry() so that we can spot
556
                     * unexpected removals of entries from the cache,
557
                     * and set the restart_scan flag if proceeding
558
                     * would be likely to cause us to scan an entry
559
                     * that is no longer in the cache.
560
                     */
561
0
                    cache_ptr->entries_removed_counter = 0;
562
0
                    cache_ptr->last_entry_removed_ptr  = NULL;
563
564
0
                    if (H5C__flush_single_entry(f, entry_ptr, H5C__NO_FLAGS_SET) < 0)
565
0
                        HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to flush entry");
566
567
0
                    if (cache_ptr->entries_removed_counter > 1 ||
568
0
                        cache_ptr->last_entry_removed_ptr == prev_ptr)
569
0
                        restart_scan = true;
570
0
                } /* end else */
571
0
            }     /* end if */
572
0
            else if (!entry_ptr->prefetched_dirty) {
573
0
                bytes_evicted += entry_ptr->size;
574
575
0
                if (H5C__flush_single_entry(
576
0
                        f, entry_ptr, H5C__FLUSH_INVALIDATE_FLAG | H5C__DEL_FROM_SLIST_ON_DESTROY_FLAG) < 0)
577
0
                    HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to flush entry");
578
0
            } /* end else-if */
579
0
            else {
580
0
                assert(!entry_ptr->is_dirty);
581
0
                assert(entry_ptr->prefetched_dirty);
582
583
0
                skipping_entry = true;
584
0
            } /* end else */
585
586
0
            if (prev_ptr != NULL) {
587
0
                if (skipping_entry)
588
0
                    entry_ptr = prev_ptr;
589
0
                else if (restart_scan || (prev_ptr->is_dirty != prev_is_dirty) ||
590
0
                         (prev_ptr->next != next_ptr) || (prev_ptr->is_protected) || (prev_ptr->is_pinned)) {
591
                    /* Something has happened to the LRU -- start over
592
                     * from the tail.
593
                     */
594
0
                    restart_scan = false;
595
0
                    entry_ptr    = cache_ptr->LRU_tail_ptr;
596
597
0
                    H5C__UPDATE_STATS_FOR_LRU_SCAN_RESTART(cache_ptr);
598
0
                } /* end else-if */
599
0
                else
600
0
                    entry_ptr = prev_ptr;
601
0
            } /* end if */
602
0
            else
603
0
                entry_ptr = NULL;
604
0
        } /* end while */
605
606
        /* for now at least, don't bother to maintain the minimum clean size,
607
         * as the cache should now be less than its maximum size.  Due to
608
         * the vaguries of the cache size reduction algorithm, we may not
609
         * reduce the size of the cache.
610
         *
611
         * If we do, we will calculate a new minimum clean size, which will
612
         * be enforced the next time we try to make space in the cache.
613
         *
614
         * If we don't, no action is necessary, as we have just evicted and/or
615
         * or flushed a bunch of entries and therefore the sum of the clean
616
         * and free space in the cache must be greater than or equal to the
617
         * min clean space requirement (assuming that requirement was met on
618
         * entry).
619
         */
620
0
    } /* end if */
621
0
    else /* ! write_permitted */ {
622
        /* Since we are not allowed to write, all we can do is evict
623
         * any clean entries that we may encounter before we either
624
         * hit the eviction size limit, or encounter the epoch marker.
625
         *
626
         * If we are operating read only, this isn't an issue, as there
627
         * will not be any dirty entries.
628
         *
629
         * If we are operating in R/W mode, all the dirty entries we
630
         * skip will be flushed the next time we attempt to make space
631
         * when writes are permitted.  This may have some local
632
         * performance implications, but it shouldn't cause any net
633
         * slowdown.
634
         */
635
0
        assert(H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS);
636
0
        entry_ptr = cache_ptr->LRU_tail_ptr;
637
0
        while (entry_ptr != NULL && ((entry_ptr->type)->id != H5AC_EPOCH_MARKER_ID) &&
638
0
               (bytes_evicted < eviction_size_limit)) {
639
0
            assert(!(entry_ptr->is_protected));
640
641
0
            prev_ptr = entry_ptr->prev;
642
643
0
            if (!(entry_ptr->is_dirty) && !(entry_ptr->prefetched_dirty))
644
0
                if (H5C__flush_single_entry(
645
0
                        f, entry_ptr, H5C__FLUSH_INVALIDATE_FLAG | H5C__DEL_FROM_SLIST_ON_DESTROY_FLAG) < 0)
646
0
                    HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to flush clean entry");
647
648
            /* just skip the entry if it is dirty, as we can't do
649
             * anything with it now since we can't write.
650
             *
651
             * Since all entries are clean, serialize() will not be called,
652
             * and thus we needn't test to see if the LRU has been changed
653
             * out from under us.
654
             */
655
0
            entry_ptr = prev_ptr;
656
0
        } /* end while */
657
0
    }     /* end else */
658
659
0
    if (cache_ptr->index_size < cache_ptr->max_cache_size)
660
0
        cache_ptr->cache_full = false;
661
662
0
done:
663
0
    FUNC_LEAVE_NOAPI(ret_value)
664
0
} /* H5C__autoadjust__ageout__evict_aged_out_entries() */
665
666
/*-------------------------------------------------------------------------
667
 * Function:    H5C__autoadjust__ageout__insert_new_marker
668
 *
669
 * Purpose:     Find an unused marker cache entry, mark it as used, and
670
 *        insert it at the head of the LRU list.  Also add the
671
 *        marker's index in the epoch_markers array.
672
 *
673
 * Return:      SUCCEED on success/FAIL on failure.
674
 *
675
 *-------------------------------------------------------------------------
676
 */
677
static herr_t
678
H5C__autoadjust__ageout__insert_new_marker(H5C_t *cache_ptr)
679
0
{
680
0
    int    i;
681
0
    herr_t ret_value = SUCCEED; /* Return value */
682
683
0
    FUNC_ENTER_PACKAGE
684
685
0
    assert(cache_ptr);
686
687
0
    if (cache_ptr->epoch_markers_active >= cache_ptr->resize_ctl.epochs_before_eviction)
688
0
        HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Already have a full complement of markers");
689
690
    /* find an unused marker */
691
0
    i = 0;
692
0
    while (i < H5C__MAX_EPOCH_MARKERS && (cache_ptr->epoch_marker_active)[i])
693
0
        i++;
694
0
    if (i >= H5C__MAX_EPOCH_MARKERS)
695
0
        HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Can't find unused marker");
696
697
0
    assert(((cache_ptr->epoch_markers)[i]).addr == (haddr_t)i);
698
0
    assert(((cache_ptr->epoch_markers)[i]).next == NULL);
699
0
    assert(((cache_ptr->epoch_markers)[i]).prev == NULL);
700
701
0
    (cache_ptr->epoch_marker_active)[i] = true;
702
703
0
    cache_ptr->epoch_marker_ringbuf_last =
704
0
        (cache_ptr->epoch_marker_ringbuf_last + 1) % (H5C__MAX_EPOCH_MARKERS + 1);
705
0
    (cache_ptr->epoch_marker_ringbuf)[cache_ptr->epoch_marker_ringbuf_last] = i;
706
0
    if (cache_ptr->epoch_marker_ringbuf_size >= H5C__MAX_EPOCH_MARKERS)
707
0
        HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "ring buffer overflow");
708
709
0
    cache_ptr->epoch_marker_ringbuf_size += 1;
710
711
0
    H5C__DLL_PREPEND(&(cache_ptr->epoch_markers[i]), cache_ptr->LRU_head_ptr, cache_ptr->LRU_tail_ptr,
712
0
                     cache_ptr->LRU_list_len, cache_ptr->LRU_list_size, FAIL)
713
714
0
    cache_ptr->epoch_markers_active += 1;
715
716
0
done:
717
0
    FUNC_LEAVE_NOAPI(ret_value)
718
0
} /* H5C__autoadjust__ageout__insert_new_marker() */
719
720
/*-------------------------------------------------------------------------
721
 * Function:    H5C__autoadjust__ageout__remove_all_markers
722
 *
723
 * Purpose:     Remove all epoch markers from the LRU list and mark them
724
 *              as inactive.
725
 *
726
 * Return:      SUCCEED on success/FAIL on failure.
727
 *
728
 *-------------------------------------------------------------------------
729
 */
730
herr_t
731
H5C__autoadjust__ageout__remove_all_markers(H5C_t *cache_ptr)
732
0
{
733
0
    int    ring_buf_index;
734
0
    int    i;
735
0
    herr_t ret_value = SUCCEED; /* Return value */
736
737
0
    FUNC_ENTER_PACKAGE
738
739
0
    assert(cache_ptr);
740
741
0
    while (cache_ptr->epoch_markers_active > 0) {
742
        /* get the index of the last epoch marker in the LRU list
743
         * and remove it from the ring buffer.
744
         */
745
746
0
        ring_buf_index = cache_ptr->epoch_marker_ringbuf_first;
747
0
        i              = (cache_ptr->epoch_marker_ringbuf)[ring_buf_index];
748
749
0
        cache_ptr->epoch_marker_ringbuf_first =
750
0
            (cache_ptr->epoch_marker_ringbuf_first + 1) % (H5C__MAX_EPOCH_MARKERS + 1);
751
752
0
        if (cache_ptr->epoch_marker_ringbuf_size <= 0)
753
0
            HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "ring buffer underflow");
754
0
        cache_ptr->epoch_marker_ringbuf_size -= 1;
755
756
0
        if (cache_ptr->epoch_marker_active[i] != true)
757
0
            HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "unused marker in LRU?!?");
758
759
        /* remove the epoch marker from the LRU list */
760
0
        H5C__DLL_REMOVE(&(cache_ptr->epoch_markers[i]), cache_ptr->LRU_head_ptr, cache_ptr->LRU_tail_ptr,
761
0
                        cache_ptr->LRU_list_len, cache_ptr->LRU_list_size, FAIL)
762
763
        /* mark the epoch marker as unused. */
764
0
        cache_ptr->epoch_marker_active[i] = false;
765
766
0
        assert(cache_ptr->epoch_markers[i].addr == (haddr_t)i);
767
0
        assert(cache_ptr->epoch_markers[i].next == NULL);
768
0
        assert(cache_ptr->epoch_markers[i].prev == NULL);
769
770
        /* decrement the number of active epoch markers */
771
0
        cache_ptr->epoch_markers_active -= 1;
772
773
0
        assert(cache_ptr->epoch_markers_active == cache_ptr->epoch_marker_ringbuf_size);
774
0
    }
775
776
0
done:
777
0
    FUNC_LEAVE_NOAPI(ret_value)
778
0
} /* H5C__autoadjust__ageout__remove_all_markers() */
779
780
/*-------------------------------------------------------------------------
781
 * Function:    H5C__autoadjust__ageout__remove_excess_markers
782
 *
783
 * Purpose:     Remove epoch markers from the end of the LRU list and
784
 *        mark them as inactive until the number of active markers
785
 *        equals the current value of
786
 *        cache_ptr->resize_ctl.epochs_before_eviction.
787
 *
788
 * Return:      SUCCEED on success/FAIL on failure.
789
 *
790
 *-------------------------------------------------------------------------
791
 */
792
herr_t
793
H5C__autoadjust__ageout__remove_excess_markers(H5C_t *cache_ptr)
794
0
{
795
0
    int    ring_buf_index;
796
0
    int    i;
797
0
    herr_t ret_value = SUCCEED; /* Return value */
798
799
0
    FUNC_ENTER_PACKAGE
800
801
0
    assert(cache_ptr);
802
803
0
    if (cache_ptr->epoch_markers_active <= cache_ptr->resize_ctl.epochs_before_eviction)
804
0
        HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "no excess markers on entry");
805
806
0
    while (cache_ptr->epoch_markers_active > cache_ptr->resize_ctl.epochs_before_eviction) {
807
        /* get the index of the last epoch marker in the LRU list
808
         * and remove it from the ring buffer.
809
         */
810
0
        ring_buf_index = cache_ptr->epoch_marker_ringbuf_first;
811
0
        i              = (cache_ptr->epoch_marker_ringbuf)[ring_buf_index];
812
813
0
        cache_ptr->epoch_marker_ringbuf_first =
814
0
            (cache_ptr->epoch_marker_ringbuf_first + 1) % (H5C__MAX_EPOCH_MARKERS + 1);
815
816
0
        if (cache_ptr->epoch_marker_ringbuf_size <= 0)
817
0
            HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "ring buffer underflow");
818
0
        cache_ptr->epoch_marker_ringbuf_size -= 1;
819
820
0
        if (cache_ptr->epoch_marker_active[i] != true)
821
0
            HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "unused marker in LRU?!?");
822
823
        /* remove the epoch marker from the LRU list */
824
0
        H5C__DLL_REMOVE(&(cache_ptr->epoch_markers[i]), cache_ptr->LRU_head_ptr, cache_ptr->LRU_tail_ptr,
825
0
                        cache_ptr->LRU_list_len, cache_ptr->LRU_list_size, FAIL)
826
827
        /* mark the epoch marker as unused. */
828
0
        cache_ptr->epoch_marker_active[i] = false;
829
830
0
        assert(cache_ptr->epoch_markers[i].addr == (haddr_t)i);
831
0
        assert(cache_ptr->epoch_markers[i].next == NULL);
832
0
        assert(cache_ptr->epoch_markers[i].prev == NULL);
833
834
        /* decrement the number of active epoch markers */
835
0
        cache_ptr->epoch_markers_active -= 1;
836
837
0
        assert(cache_ptr->epoch_markers_active == cache_ptr->epoch_marker_ringbuf_size);
838
0
    }
839
840
0
done:
841
0
    FUNC_LEAVE_NOAPI(ret_value)
842
0
} /* H5C__autoadjust__ageout__remove_excess_markers() */
843
844
/*-------------------------------------------------------------------------
845
 * Function:    H5C__flash_increase_cache_size
846
 *
847
 * Purpose:     If there is not at least new_entry_size - old_entry_size
848
 *              bytes of free space in the cache and the current
849
 *              max_cache_size is less than cache_ptr->resize_ctl.max_size,
850
 *              perform a flash increase in the cache size and then reset
851
 *              the full cache hit rate statistics, and exit.
852
 *
853
 * Return:      Non-negative on success/Negative on failure.
854
 *
855
 *-------------------------------------------------------------------------
856
 */
857
herr_t
858
H5C__flash_increase_cache_size(H5C_t *cache_ptr, size_t old_entry_size, size_t new_entry_size)
859
6
{
860
6
    size_t                 new_max_cache_size = 0;
861
6
    size_t                 old_max_cache_size = 0;
862
6
    size_t                 new_min_clean_size = 0;
863
6
    size_t                 old_min_clean_size = 0;
864
6
    size_t                 space_needed;
865
6
    enum H5C_resize_status status = flash_increase; /* may change */
866
6
    double                 hit_rate;
867
6
    herr_t                 ret_value = SUCCEED; /* Return value */
868
869
6
    FUNC_ENTER_PACKAGE
870
871
6
    assert(cache_ptr);
872
6
    assert(cache_ptr->flash_size_increase_possible);
873
6
    assert(new_entry_size > cache_ptr->flash_size_increase_threshold);
874
6
    assert(old_entry_size < new_entry_size);
875
876
6
    if (old_entry_size >= new_entry_size)
877
0
        HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "old_entry_size >= new_entry_size");
878
879
6
    space_needed = new_entry_size - old_entry_size;
880
6
    if (((cache_ptr->index_size + space_needed) > cache_ptr->max_cache_size) &&
881
6
        (cache_ptr->max_cache_size < cache_ptr->resize_ctl.max_size)) {
882
6
        switch (cache_ptr->resize_ctl.flash_incr_mode) {
883
0
            case H5C_flash_incr__off:
884
0
                HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL,
885
0
                            "flash_size_increase_possible but H5C_flash_incr__off?!");
886
0
                break;
887
888
6
            case H5C_flash_incr__add_space:
889
6
                if (cache_ptr->index_size < cache_ptr->max_cache_size) {
890
6
                    assert((cache_ptr->max_cache_size - cache_ptr->index_size) < space_needed);
891
6
                    space_needed -= cache_ptr->max_cache_size - cache_ptr->index_size;
892
6
                }
893
6
                space_needed       = (size_t)(((double)space_needed) * cache_ptr->resize_ctl.flash_multiple);
894
6
                new_max_cache_size = cache_ptr->max_cache_size + space_needed;
895
6
                break;
896
897
0
            default: /* should be unreachable */
898
0
                HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Unknown flash_incr_mode?!?!?");
899
0
                break;
900
6
        }
901
902
6
        if (new_max_cache_size > cache_ptr->resize_ctl.max_size)
903
6
            new_max_cache_size = cache_ptr->resize_ctl.max_size;
904
6
        assert(new_max_cache_size > cache_ptr->max_cache_size);
905
906
6
        new_min_clean_size = (size_t)((double)new_max_cache_size * cache_ptr->resize_ctl.min_clean_fraction);
907
6
        assert(new_min_clean_size <= new_max_cache_size);
908
909
6
        old_max_cache_size = cache_ptr->max_cache_size;
910
6
        old_min_clean_size = cache_ptr->min_clean_size;
911
912
6
        cache_ptr->max_cache_size = new_max_cache_size;
913
6
        cache_ptr->min_clean_size = new_min_clean_size;
914
915
        /* update flash cache size increase fields as appropriate */
916
6
        assert(cache_ptr->flash_size_increase_possible);
917
918
6
        switch (cache_ptr->resize_ctl.flash_incr_mode) {
919
0
            case H5C_flash_incr__off:
920
0
                HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL,
921
0
                            "flash_size_increase_possible but H5C_flash_incr__off?!");
922
0
                break;
923
924
6
            case H5C_flash_incr__add_space:
925
6
                cache_ptr->flash_size_increase_threshold =
926
6
                    (size_t)((double)cache_ptr->max_cache_size * cache_ptr->resize_ctl.flash_threshold);
927
6
                break;
928
929
0
            default: /* should be unreachable */
930
0
                HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Unknown flash_incr_mode?!?!?");
931
0
                break;
932
6
        }
933
934
        /* note that we don't cycle the epoch markers.  We can
935
         * argue either way as to whether we should, but for now
936
         * we don't.
937
         */
938
939
6
        if (cache_ptr->resize_ctl.rpt_fcn != NULL) {
940
            /* get the hit rate for the reporting function.  Should still
941
             * be good as we haven't reset the hit rate statistics.
942
             */
943
0
            if (H5C_get_cache_hit_rate(cache_ptr, &hit_rate) != SUCCEED)
944
0
                HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Can't get hit rate");
945
946
0
            (cache_ptr->resize_ctl.rpt_fcn)(cache_ptr, H5C__CURR_AUTO_RESIZE_RPT_FCN_VER, hit_rate, status,
947
0
                                            old_max_cache_size, new_max_cache_size, old_min_clean_size,
948
0
                                            new_min_clean_size);
949
0
        }
950
951
6
        if (H5C_reset_cache_hit_rate_stats(cache_ptr) < 0)
952
            /* this should be impossible... */
953
0
            HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "H5C_reset_cache_hit_rate_stats failed");
954
6
    }
955
956
6
done:
957
6
    FUNC_LEAVE_NOAPI(ret_value)
958
6
} /* H5C__flash_increase_cache_size() */
959
960
/*-------------------------------------------------------------------------
961
 * Function:    H5C__flush_invalidate_cache
962
 *
963
 * Purpose:    Flush and destroy the entries contained in the target
964
 *        cache.
965
 *
966
 *        If the cache contains protected entries, the function will
967
 *        fail, as protected entries cannot be either flushed or
968
 *        destroyed.  However all unprotected entries should be
969
 *        flushed and destroyed before the function returns failure.
970
 *
971
 *        While pinned entries can usually be flushed, they cannot
972
 *        be destroyed.  However, they should be unpinned when all
973
 *        the entries that reference them have been destroyed (thus
974
 *        reduding the pinned entry's reference count to 0, allowing
975
 *        it to be unpinned).
976
 *
977
 *        If pinned entries are present, the function makes repeated
978
 *        passes through the cache, flushing all dirty entries
979
 *        (including the pinned dirty entries where permitted) and
980
 *        destroying all unpinned entries.  This process is repeated
981
 *        until either the cache is empty, or the number of pinned
982
 *        entries stops decreasing on each pass.
983
 *
984
 * Return:      Non-negative on success/Negative on failure or if there was
985
 *        a request to flush all items and something was protected.
986
 *
987
 *-------------------------------------------------------------------------
988
 */
989
herr_t
990
H5C__flush_invalidate_cache(H5F_t *f, unsigned flags)
991
15
{
992
15
    H5C_t     *cache_ptr;
993
15
    H5C_ring_t ring;
994
15
    herr_t     ret_value = SUCCEED;
995
996
15
    FUNC_ENTER_PACKAGE
997
998
15
    assert(f);
999
15
    assert(f->shared);
1000
15
    cache_ptr = f->shared->cache;
1001
15
    assert(cache_ptr);
1002
15
    assert(cache_ptr->slist_ptr);
1003
15
    assert(cache_ptr->slist_enabled);
1004
1005
#ifdef H5C_DO_SANITY_CHECKS
1006
    {
1007
        int32_t  i;
1008
        uint32_t index_len        = 0;
1009
        uint32_t slist_len        = 0;
1010
        size_t   index_size       = (size_t)0;
1011
        size_t   clean_index_size = (size_t)0;
1012
        size_t   dirty_index_size = (size_t)0;
1013
        size_t   slist_size       = (size_t)0;
1014
1015
        assert(cache_ptr->index_ring_len[H5C_RING_UNDEFINED] == 0);
1016
        assert(cache_ptr->index_ring_size[H5C_RING_UNDEFINED] == (size_t)0);
1017
        assert(cache_ptr->clean_index_ring_size[H5C_RING_UNDEFINED] == (size_t)0);
1018
        assert(cache_ptr->dirty_index_ring_size[H5C_RING_UNDEFINED] == (size_t)0);
1019
        assert(cache_ptr->slist_ring_len[H5C_RING_UNDEFINED] == 0);
1020
        assert(cache_ptr->slist_ring_size[H5C_RING_UNDEFINED] == (size_t)0);
1021
1022
        for (i = H5C_RING_USER; i < H5C_RING_NTYPES; i++) {
1023
            index_len += cache_ptr->index_ring_len[i];
1024
            index_size += cache_ptr->index_ring_size[i];
1025
            clean_index_size += cache_ptr->clean_index_ring_size[i];
1026
            dirty_index_size += cache_ptr->dirty_index_ring_size[i];
1027
1028
            slist_len += cache_ptr->slist_ring_len[i];
1029
            slist_size += cache_ptr->slist_ring_size[i];
1030
        } /* end for */
1031
1032
        assert(cache_ptr->index_len == index_len);
1033
        assert(cache_ptr->index_size == index_size);
1034
        assert(cache_ptr->clean_index_size == clean_index_size);
1035
        assert(cache_ptr->dirty_index_size == dirty_index_size);
1036
        assert(cache_ptr->slist_len == slist_len);
1037
        assert(cache_ptr->slist_size == slist_size);
1038
    }
1039
#endif /* H5C_DO_SANITY_CHECKS */
1040
1041
    /* remove ageout markers if present */
1042
15
    if (cache_ptr->epoch_markers_active > 0)
1043
0
        if (H5C__autoadjust__ageout__remove_all_markers(cache_ptr) < 0)
1044
0
            HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "error removing all epoch markers");
1045
1046
    /* flush invalidate each ring, starting from the outermost ring and
1047
     * working inward.
1048
     */
1049
15
    ring = H5C_RING_USER;
1050
78
    while (ring < H5C_RING_NTYPES) {
1051
69
        if (H5C__flush_invalidate_ring(f, ring, flags) < 0)
1052
6
            HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "flush invalidate ring failed");
1053
63
        ring++;
1054
63
    } /* end while */
1055
1056
#ifndef NDEBUG
1057
    /* Invariants, after destroying all entries in the hash table */
1058
    if (!(flags & H5C__EVICT_ALLOW_LAST_PINS_FLAG)) {
1059
        assert(cache_ptr->index_size == 0);
1060
        assert(cache_ptr->clean_index_size == 0);
1061
        assert(cache_ptr->pel_len == 0);
1062
        assert(cache_ptr->pel_size == 0);
1063
    } /* end if */
1064
    else {
1065
        H5C_cache_entry_t *entry_ptr; /* Cache entry */
1066
        unsigned           u;         /* Local index variable */
1067
1068
        /* All rings except ring 4 should be empty now */
1069
        /* (Ring 4 has the superblock) */
1070
        for (u = H5C_RING_USER; u < H5C_RING_SB; u++) {
1071
            assert(cache_ptr->index_ring_len[u] == 0);
1072
            assert(cache_ptr->index_ring_size[u] == 0);
1073
            assert(cache_ptr->clean_index_ring_size[u] == 0);
1074
        } /* end for */
1075
1076
        /* Check that any remaining pinned entries are in the superblock ring */
1077
        entry_ptr = cache_ptr->pel_head_ptr;
1078
        while (entry_ptr) {
1079
            /* Check ring */
1080
            assert(entry_ptr->ring == H5C_RING_SB);
1081
1082
            /* Advance to next entry in pinned entry list */
1083
            entry_ptr = entry_ptr->next;
1084
        } /* end while */
1085
    }     /* end else */
1086
1087
    assert(cache_ptr->dirty_index_size == 0);
1088
    assert(cache_ptr->slist_len == 0);
1089
    assert(cache_ptr->slist_size == 0);
1090
    assert(cache_ptr->pl_len == 0);
1091
    assert(cache_ptr->pl_size == 0);
1092
    assert(cache_ptr->LRU_list_len == 0);
1093
    assert(cache_ptr->LRU_list_size == 0);
1094
#endif
1095
1096
15
done:
1097
15
    FUNC_LEAVE_NOAPI(ret_value)
1098
15
} /* H5C__flush_invalidate_cache() */
1099
1100
/*-------------------------------------------------------------------------
1101
 * Function:    H5C__flush_invalidate_ring
1102
 *
1103
 * Purpose:     Flush and destroy the entries contained in the target
1104
 *              cache and ring.
1105
 *
1106
 *              If the ring contains protected entries, the function will
1107
 *              fail, as protected entries cannot be either flushed or
1108
 *              destroyed.  However all unprotected entries should be
1109
 *              flushed and destroyed before the function returns failure.
1110
 *
1111
 *              While pinned entries can usually be flushed, they cannot
1112
 *              be destroyed.  However, they should be unpinned when all
1113
 *              the entries that reference them have been destroyed (thus
1114
 *              reduding the pinned entry's reference count to 0, allowing
1115
 *              it to be unpinned).
1116
 *
1117
 *              If pinned entries are present, the function makes repeated
1118
 *              passes through the cache, flushing all dirty entries
1119
 *              (including the pinned dirty entries where permitted) and
1120
 *              destroying all unpinned entries.  This process is repeated
1121
 *              until either the cache is empty, or the number of pinned
1122
 *              entries stops decreasing on each pass.
1123
 *
1124
 *              If flush dependencies appear in the target ring, the
1125
 *              function makes repeated passes through the cache flushing
1126
 *              entries in flush dependency order.
1127
 *
1128
 * Return:      Non-negative on success/Negative on failure or if there was
1129
 *              a request to flush all items and something was protected.
1130
 *
1131
 *-------------------------------------------------------------------------
1132
 */
1133
static herr_t
1134
H5C__flush_invalidate_ring(H5F_t *f, H5C_ring_t ring, unsigned flags)
1135
69
{
1136
69
    H5C_t             *cache_ptr;
1137
69
    bool               restart_slist_scan;
1138
69
    uint32_t           protected_entries = 0;
1139
69
    int32_t            i;
1140
69
    uint32_t           cur_ring_pel_len;
1141
69
    uint32_t           old_ring_pel_len;
1142
69
    unsigned           cooked_flags;
1143
69
    unsigned           evict_flags;
1144
69
    H5SL_node_t       *node_ptr       = NULL;
1145
69
    H5C_cache_entry_t *entry_ptr      = NULL;
1146
69
    H5C_cache_entry_t *next_entry_ptr = NULL;
1147
#ifdef H5C_DO_SANITY_CHECKS
1148
    uint32_t initial_slist_len  = 0;
1149
    size_t   initial_slist_size = 0;
1150
#endif /* H5C_DO_SANITY_CHECKS */
1151
69
    herr_t ret_value = SUCCEED;
1152
1153
69
    FUNC_ENTER_PACKAGE
1154
1155
69
    assert(f);
1156
69
    assert(f->shared);
1157
69
    cache_ptr = f->shared->cache;
1158
69
    assert(cache_ptr);
1159
69
    assert(cache_ptr->slist_enabled);
1160
69
    assert(cache_ptr->slist_ptr);
1161
69
    assert(ring > H5C_RING_UNDEFINED);
1162
69
    assert(ring < H5C_RING_NTYPES);
1163
1164
69
    assert(cache_ptr->epoch_markers_active == 0);
1165
1166
    /* Filter out the flags that are not relevant to the flush/invalidate.
1167
     */
1168
69
    cooked_flags = flags & H5C__FLUSH_CLEAR_ONLY_FLAG;
1169
69
    evict_flags  = flags & H5C__EVICT_ALLOW_LAST_PINS_FLAG;
1170
1171
    /* The flush procedure here is a bit strange.
1172
     *
1173
     * In the outer while loop we make at least one pass through the
1174
     * cache, and then repeat until either all the pinned entries in
1175
     * the ring unpin themselves, or until the number of pinned entries
1176
     * in the ring stops declining.  In this later case, we scream and die.
1177
     *
1178
     * Since the fractal heap can dirty, resize, and/or move entries
1179
     * in is flush callback, it is possible that the cache will still
1180
     * contain dirty entries at this point.  If so, we must make more
1181
     * passes through the skip list to allow it to empty.
1182
     *
1183
     * Further, since clean entries can be dirtied, resized, and/or moved
1184
     * as the result of a flush call back (either the entries own, or that
1185
     * for some other cache entry), we can no longer promise to flush
1186
     * the cache entries in increasing address order.
1187
     *
1188
     * Instead, we make a pass through
1189
     * the skip list, and then a pass through the "clean" entries, and
1190
     * then repeating as needed.  Thus it is quite possible that an
1191
     * entry will be evicted from the cache only to be re-loaded later
1192
     * in the flush process.
1193
     *
1194
     * The bottom line is that entries will probably be flushed in close
1195
     * to increasing address order, but there are no guarantees.
1196
     */
1197
1198
    /* compute the number of pinned entries in this ring */
1199
69
    entry_ptr        = cache_ptr->pel_head_ptr;
1200
69
    cur_ring_pel_len = 0;
1201
69
    while (entry_ptr != NULL) {
1202
0
        assert(entry_ptr->ring >= ring);
1203
0
        if (entry_ptr->ring == ring)
1204
0
            cur_ring_pel_len++;
1205
1206
0
        entry_ptr = entry_ptr->next;
1207
0
    } /* end while */
1208
69
    old_ring_pel_len = cur_ring_pel_len;
1209
1210
75
    while (cache_ptr->index_ring_len[ring] > 0) {
1211
        /* First, try to flush-destroy any dirty entries.   Do this by
1212
         * making a scan through the slist.  Note that new dirty entries
1213
         * may be created by the flush call back, thus we may need to
1214
         * restart the scan (see below).
1215
         */
1216
1217
#ifdef H5C_DO_SANITY_CHECKS
1218
        /* Depending on circumstances, H5C__flush_single_entry() will
1219
         * remove dirty entries from the slist as it flushes them.
1220
         * Thus for sanity checks we must make note of the initial
1221
         * slist length and size before we do any flushes.
1222
         */
1223
        initial_slist_len  = cache_ptr->slist_len;
1224
        initial_slist_size = cache_ptr->slist_size;
1225
1226
        /* There is also the possibility that entries will be
1227
         * dirtied, resized, moved, and/or removed from the cache
1228
         * as the result of calls to the flush callbacks.  We use
1229
         * the slist_len_increase and slist_size_increase increase
1230
         * fields in struct H5C_t to track these changes for purpose
1231
         * of sanity checking.
1232
         *
1233
         * To this end, we must zero these fields before we start
1234
         * the pass through the slist.
1235
         */
1236
        cache_ptr->slist_len_increase  = 0;
1237
        cache_ptr->slist_size_increase = 0;
1238
#endif /* H5C_DO_SANITY_CHECKS */
1239
1240
        /* Set the cache_ptr->slist_changed to false.
1241
         *
1242
         * This flag is set to true by H5C__flush_single_entry if the slist
1243
         * is modified by a pre_serialize, serialize, or notify callback.
1244
         *
1245
         * H5C__flush_invalidate_ring() uses this flag to detect any
1246
         * modifications to the slist that might corrupt the scan of
1247
         * the slist -- and restart the scan in this event.
1248
         */
1249
12
        cache_ptr->slist_changed = false;
1250
1251
        /* this done, start the scan of the slist */
1252
12
        restart_slist_scan = true;
1253
30
        while (restart_slist_scan || (node_ptr != NULL)) {
1254
24
            if (restart_slist_scan) {
1255
12
                restart_slist_scan = false;
1256
1257
                /* Start at beginning of skip list */
1258
12
                node_ptr = H5SL_first(cache_ptr->slist_ptr);
1259
12
                if (node_ptr == NULL)
1260
                    /* the slist is empty -- break out of inner loop */
1261
0
                    break;
1262
1263
                /* Get cache entry for this node */
1264
12
                next_entry_ptr = (H5C_cache_entry_t *)H5SL_item(node_ptr);
1265
12
                if (NULL == next_entry_ptr)
1266
0
                    HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "next_entry_ptr == NULL ?!?!");
1267
1268
12
                assert(next_entry_ptr->is_dirty);
1269
12
                assert(next_entry_ptr->in_slist);
1270
12
                assert(next_entry_ptr->ring >= ring);
1271
12
            } /* end if */
1272
1273
24
            entry_ptr = next_entry_ptr;
1274
1275
            /* It is possible that entries will be dirtied, resized,
1276
             * flushed, or removed from the cache via the take ownership
1277
             * flag as the result of pre_serialize or serialized callbacks.
1278
             *
1279
             * This in turn can corrupt the scan through the slist.
1280
             *
1281
             * We test for slist modifications in the pre_serialize
1282
             * and serialize callbacks, and restart the scan of the
1283
             * slist if we find them.  However, best we do some extra
1284
             * sanity checking just in case.
1285
             */
1286
24
            assert(entry_ptr != NULL);
1287
24
            assert(entry_ptr->in_slist);
1288
24
            assert(entry_ptr->is_dirty);
1289
24
            assert(entry_ptr->ring >= ring);
1290
1291
            /* increment node pointer now, before we delete its target
1292
             * from the slist.
1293
             */
1294
24
            node_ptr = H5SL_next(node_ptr);
1295
24
            if (node_ptr != NULL) {
1296
12
                next_entry_ptr = (H5C_cache_entry_t *)H5SL_item(node_ptr);
1297
12
                if (NULL == next_entry_ptr)
1298
0
                    HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "next_entry_ptr == NULL ?!?!");
1299
1300
12
                assert(next_entry_ptr->is_dirty);
1301
12
                assert(next_entry_ptr->in_slist);
1302
12
                assert(next_entry_ptr->ring >= ring);
1303
12
                assert(entry_ptr != next_entry_ptr);
1304
12
            } /* end if */
1305
12
            else
1306
12
                next_entry_ptr = NULL;
1307
1308
            /* Note that we now remove nodes from the slist as we flush
1309
             * the associated entries, instead of leaving them there
1310
             * until we are done, and then destroying all nodes in
1311
             * the slist.
1312
             *
1313
             * While this optimization used to be easy, with the possibility
1314
             * of new entries being added to the slist in the midst of the
1315
             * flush, we must keep the slist in canonical form at all
1316
             * times.
1317
             */
1318
24
            if (((!entry_ptr->flush_me_last) ||
1319
24
                 ((entry_ptr->flush_me_last) && (cache_ptr->num_last_entries >= cache_ptr->slist_len))) &&
1320
24
                (entry_ptr->flush_dep_nchildren == 0) && (entry_ptr->ring == ring)) {
1321
6
                if (entry_ptr->is_protected) {
1322
                    /* We have major problems -- but lets flush
1323
                     * everything we can before we flag an error.
1324
                     */
1325
0
                    protected_entries++;
1326
0
                } /* end if */
1327
6
                else if (entry_ptr->is_pinned) {
1328
0
                    if (H5C__flush_single_entry(f, entry_ptr, H5C__DURING_FLUSH_FLAG) < 0)
1329
0
                        HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "dirty pinned entry flush failed");
1330
1331
0
                    if (cache_ptr->slist_changed) {
1332
                        /* The slist has been modified by something
1333
                         * other than the simple removal of the
1334
                         * of the flushed entry after the flush.
1335
                         *
1336
                         * This has the potential to corrupt the
1337
                         * scan through the slist, so restart it.
1338
                         */
1339
0
                        restart_slist_scan       = true;
1340
0
                        cache_ptr->slist_changed = false;
1341
0
                        H5C__UPDATE_STATS_FOR_SLIST_SCAN_RESTART(cache_ptr);
1342
0
                    } /* end if */
1343
0
                }     /* end else-if */
1344
6
                else {
1345
6
                    if (H5C__flush_single_entry(f, entry_ptr,
1346
6
                                                (cooked_flags | H5C__DURING_FLUSH_FLAG |
1347
6
                                                 H5C__FLUSH_INVALIDATE_FLAG |
1348
6
                                                 H5C__DEL_FROM_SLIST_ON_DESTROY_FLAG)) < 0)
1349
6
                        HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "dirty entry flush destroy failed");
1350
1351
0
                    if (cache_ptr->slist_changed) {
1352
                        /* The slist has been modified by something
1353
                         * other than the simple removal of the
1354
                         * of the flushed entry after the flush.
1355
                         *
1356
                         * This has the potential to corrupt the
1357
                         * scan through the slist, so restart it.
1358
                         */
1359
0
                        restart_slist_scan       = true;
1360
0
                        cache_ptr->slist_changed = false;
1361
0
                        H5C__UPDATE_STATS_FOR_SLIST_SCAN_RESTART(cache_ptr);
1362
0
                    } /* end if */
1363
0
                }     /* end else */
1364
6
            }         /* end if */
1365
24
        }             /* end while loop scanning skip list */
1366
1367
#ifdef H5C_DO_SANITY_CHECKS
1368
        /* It is possible that entries were added to the slist during
1369
         * the scan, either before or after scan pointer.  The following
1370
         * asserts take this into account.
1371
         *
1372
         * Don't bother with the sanity checks if node_ptr != NULL, as
1373
         * in this case we broke out of the loop because it got changed
1374
         * out from under us.
1375
         */
1376
1377
        if (node_ptr == NULL) {
1378
            assert(cache_ptr->slist_len ==
1379
                   (uint32_t)((int32_t)initial_slist_len + cache_ptr->slist_len_increase));
1380
            assert(cache_ptr->slist_size ==
1381
                   (size_t)((ssize_t)initial_slist_size + cache_ptr->slist_size_increase));
1382
        } /* end if */
1383
#endif    /* H5C_DO_SANITY_CHECKS */
1384
1385
        /* Since we are doing a destroy, we must make a pass through
1386
         * the hash table and try to flush - destroy all entries that
1387
         * remain.
1388
         *
1389
         * It used to be that all entries remaining in the cache at
1390
         * this point had to be clean, but with the fractal heap mods
1391
         * this may not be the case.  If so, we will flush entries out
1392
         * in increasing address order.
1393
         *
1394
         * Writes to disk are possible here.
1395
         */
1396
1397
        /* Reset the counters so that we can detect insertions, loads,
1398
         * and moves caused by the pre_serialize and serialize calls.
1399
         */
1400
6
        cache_ptr->entries_loaded_counter    = 0;
1401
6
        cache_ptr->entries_inserted_counter  = 0;
1402
6
        cache_ptr->entries_relocated_counter = 0;
1403
1404
6
        next_entry_ptr = cache_ptr->il_head;
1405
24
        while (next_entry_ptr != NULL) {
1406
18
            entry_ptr = next_entry_ptr;
1407
18
            assert(entry_ptr->ring >= ring);
1408
1409
18
            next_entry_ptr = entry_ptr->il_next;
1410
1411
18
            if (((!entry_ptr->flush_me_last) ||
1412
12
                 (entry_ptr->flush_me_last && (cache_ptr->num_last_entries >= cache_ptr->slist_len))) &&
1413
18
                (entry_ptr->flush_dep_nchildren == 0) && (entry_ptr->ring == ring)) {
1414
1415
6
                if (entry_ptr->is_protected) {
1416
                    /* we have major problems -- but lets flush and
1417
                     * destroy everything we can before we flag an
1418
                     * error.
1419
                     */
1420
0
                    protected_entries++;
1421
1422
0
                    if (!entry_ptr->in_slist)
1423
0
                        assert(!(entry_ptr->is_dirty));
1424
0
                } /* end if */
1425
6
                else if (!entry_ptr->is_pinned) {
1426
                    /* if *entry_ptr is dirty, it is possible
1427
                     * that one or more other entries may be
1428
                     * either removed from the cache, loaded
1429
                     * into the cache, or moved to a new location
1430
                     * in the file as a side effect of the flush.
1431
                     *
1432
                     * It's also possible that removing a clean
1433
                     * entry will remove the last child of a proxy
1434
                     * entry, allowing it to be removed also and
1435
                     * invalidating the next_entry_ptr.
1436
                     *
1437
                     * If either of these happen, and one of the target
1438
                     * or proxy entries happens to be the next entry in
1439
                     * the hash bucket, we could either find ourselves
1440
                     * either scanning a non-existent entry, scanning
1441
                     * through a different bucket, or skipping an entry.
1442
                     *
1443
                     * Neither of these are good, so restart the
1444
                     * the scan at the head of the hash bucket
1445
                     * after the flush if we detect that the next_entry_ptr
1446
                     * becomes invalid.
1447
                     *
1448
                     * This is not as inefficient at it might seem,
1449
                     * as hash buckets typically have at most two
1450
                     * or three entries.
1451
                     */
1452
6
                    cache_ptr->entry_watched_for_removal = next_entry_ptr;
1453
6
                    if (H5C__flush_single_entry(f, entry_ptr,
1454
6
                                                (cooked_flags | H5C__DURING_FLUSH_FLAG |
1455
6
                                                 H5C__FLUSH_INVALIDATE_FLAG |
1456
6
                                                 H5C__DEL_FROM_SLIST_ON_DESTROY_FLAG)) < 0)
1457
0
                        HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "Entry flush destroy failed");
1458
1459
                    /* Restart the index list scan if necessary.  Must
1460
                     * do this if the next entry is evicted, and also if
1461
                     * one or more entries are inserted, loaded, or moved
1462
                     * as these operations can result in part of the scan
1463
                     * being skipped -- which can cause a spurious failure
1464
                     * if this results in the size of the pinned entry
1465
                     * failing to decline during the pass.
1466
                     */
1467
6
                    if (((NULL != next_entry_ptr) && (NULL == cache_ptr->entry_watched_for_removal)) ||
1468
6
                        (cache_ptr->entries_loaded_counter > 0) ||
1469
6
                        (cache_ptr->entries_inserted_counter > 0) ||
1470
6
                        (cache_ptr->entries_relocated_counter > 0)) {
1471
1472
0
                        next_entry_ptr = cache_ptr->il_head;
1473
1474
0
                        cache_ptr->entries_loaded_counter    = 0;
1475
0
                        cache_ptr->entries_inserted_counter  = 0;
1476
0
                        cache_ptr->entries_relocated_counter = 0;
1477
1478
0
                        H5C__UPDATE_STATS_FOR_INDEX_SCAN_RESTART(cache_ptr);
1479
0
                    } /* end if */
1480
6
                    else
1481
6
                        cache_ptr->entry_watched_for_removal = NULL;
1482
6
                } /* end if */
1483
6
            }     /* end if */
1484
18
        }         /* end for loop scanning hash table */
1485
1486
        /* We can't do anything if entries are pinned.  The
1487
         * hope is that the entries will be unpinned as the
1488
         * result of destroys of entries that reference them.
1489
         *
1490
         * We detect this by noting the change in the number
1491
         * of pinned entries from pass to pass.  If it stops
1492
         * shrinking before it hits zero, we scream and die.
1493
         */
1494
6
        old_ring_pel_len = cur_ring_pel_len;
1495
6
        entry_ptr        = cache_ptr->pel_head_ptr;
1496
6
        cur_ring_pel_len = 0;
1497
1498
6
        while (entry_ptr != NULL) {
1499
0
            assert(entry_ptr->ring >= ring);
1500
1501
0
            if (entry_ptr->ring == ring)
1502
0
                cur_ring_pel_len++;
1503
1504
0
            entry_ptr = entry_ptr->next;
1505
0
        } /* end while */
1506
1507
        /* Check if the number of pinned entries in the ring is positive, and
1508
         * it is not declining.  Scream and die if so.
1509
         */
1510
6
        if ((cur_ring_pel_len > 0) && (cur_ring_pel_len >= old_ring_pel_len)) {
1511
            /* Don't error if allowed to have pinned entries remaining */
1512
0
            if (evict_flags)
1513
0
                HGOTO_DONE(true);
1514
1515
0
            HGOTO_ERROR(
1516
0
                H5E_CACHE, H5E_CANTFLUSH, FAIL,
1517
0
                "Pinned entry count not decreasing, cur_ring_pel_len = %d, old_ring_pel_len = %d, ring = %d",
1518
0
                (int)cur_ring_pel_len, (int)old_ring_pel_len, (int)ring);
1519
0
        } /* end if */
1520
1521
6
        assert(protected_entries == cache_ptr->pl_len);
1522
1523
6
        if ((protected_entries > 0) && (protected_entries == cache_ptr->index_len))
1524
0
            HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL,
1525
6
                        "Only protected entries left in cache, protected_entries = %d",
1526
6
                        (int)protected_entries);
1527
6
    } /* main while loop */
1528
1529
    /* Invariants, after destroying all entries in the ring */
1530
297
    for (i = (int)H5C_RING_UNDEFINED; i <= (int)ring; i++) {
1531
234
        assert(cache_ptr->index_ring_len[i] == 0);
1532
234
        assert(cache_ptr->index_ring_size[i] == (size_t)0);
1533
234
        assert(cache_ptr->clean_index_ring_size[i] == (size_t)0);
1534
234
        assert(cache_ptr->dirty_index_ring_size[i] == (size_t)0);
1535
1536
234
        assert(cache_ptr->slist_ring_len[i] == 0);
1537
234
        assert(cache_ptr->slist_ring_size[i] == (size_t)0);
1538
234
    } /* end for */
1539
1540
63
    assert(protected_entries <= cache_ptr->pl_len);
1541
1542
63
    if (protected_entries > 0)
1543
0
        HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "Cache has protected entries");
1544
63
    else if (cur_ring_pel_len > 0)
1545
0
        HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "Can't unpin all pinned entries in ring");
1546
1547
69
done:
1548
69
    FUNC_LEAVE_NOAPI(ret_value)
1549
69
} /* H5C__flush_invalidate_ring() */
1550
1551
/*-------------------------------------------------------------------------
1552
 * Function:    H5C__flush_ring
1553
 *
1554
 * Purpose:     Flush the entries contained in the specified cache and
1555
 *              ring.  All entries in rings outside the specified ring
1556
 *              must have been flushed on entry.
1557
 *
1558
 *              If the cache contains protected entries in the specified
1559
 *              ring, the function will fail, as protected entries cannot
1560
 *              be flushed.  However all unprotected entries in the target
1561
 *              ring should be flushed before the function returns failure.
1562
 *
1563
 *              If flush dependencies appear in the target ring, the
1564
 *              function makes repeated passes through the slist flushing
1565
 *              entries in flush dependency order.
1566
 *
1567
 * Return:      Non-negative on success/Negative on failure or if there was
1568
 *              a request to flush all items and something was protected.
1569
 *
1570
 *-------------------------------------------------------------------------
1571
 */
1572
herr_t
1573
H5C__flush_ring(H5F_t *f, H5C_ring_t ring, unsigned flags)
1574
120
{
1575
120
    H5C_t             *cache_ptr = f->shared->cache;
1576
120
    bool               flushed_entries_last_pass;
1577
120
    bool               ignore_protected;
1578
120
    bool               tried_to_flush_protected_entry = false;
1579
120
    bool               restart_slist_scan;
1580
120
    uint32_t           protected_entries = 0;
1581
120
    H5SL_node_t       *node_ptr          = NULL;
1582
120
    H5C_cache_entry_t *entry_ptr         = NULL;
1583
120
    H5C_cache_entry_t *next_entry_ptr    = NULL;
1584
#ifdef H5C_DO_SANITY_CHECKS
1585
    uint32_t initial_slist_len  = 0;
1586
    size_t   initial_slist_size = 0;
1587
#endif /* H5C_DO_SANITY_CHECKS */
1588
120
    int    i;
1589
120
    herr_t ret_value = SUCCEED;
1590
1591
120
    FUNC_ENTER_PACKAGE
1592
1593
120
    assert(cache_ptr);
1594
120
    assert(cache_ptr->slist_enabled);
1595
120
    assert(cache_ptr->slist_ptr);
1596
120
    assert((flags & H5C__FLUSH_INVALIDATE_FLAG) == 0);
1597
120
    assert(ring > H5C_RING_UNDEFINED);
1598
120
    assert(ring < H5C_RING_NTYPES);
1599
1600
#ifdef H5C_DO_EXTREME_SANITY_CHECKS
1601
    if (H5C__validate_protected_entry_list(cache_ptr) < 0 || H5C__validate_pinned_entry_list(cache_ptr) < 0 ||
1602
        H5C__validate_lru_list(cache_ptr) < 0)
1603
        HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on entry");
1604
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
1605
1606
120
    ignore_protected = ((flags & H5C__FLUSH_IGNORE_PROTECTED_FLAG) != 0);
1607
1608
480
    for (i = (int)H5C_RING_UNDEFINED; i < (int)ring; i++)
1609
360
        assert(cache_ptr->slist_ring_len[i] == 0);
1610
1611
120
    assert(cache_ptr->flush_in_progress);
1612
1613
    /* When we are only flushing marked entries, the slist will usually
1614
     * still contain entries when we have flushed everything we should.
1615
     * Thus we track whether we have flushed any entries in the last
1616
     * pass, and terminate if we haven't.
1617
     */
1618
120
    flushed_entries_last_pass = true;
1619
1620
    /* Set the cache_ptr->slist_changed to false.
1621
     *
1622
     * This flag is set to true by H5C__flush_single_entry if the
1623
     * slist is modified by a pre_serialize, serialize, or notify callback.
1624
     * H5C_flush_cache uses this flag to detect any modifications
1625
     * to the slist that might corrupt the scan of the slist -- and
1626
     * restart the scan in this event.
1627
     */
1628
120
    cache_ptr->slist_changed = false;
1629
1630
126
    while ((cache_ptr->slist_ring_len[ring] > 0) && (protected_entries == 0) && (flushed_entries_last_pass)) {
1631
6
        flushed_entries_last_pass = false;
1632
1633
#ifdef H5C_DO_SANITY_CHECKS
1634
        /* For sanity checking, try to verify that the skip list has
1635
         * the expected size and number of entries at the end of each
1636
         * internal while loop (see below).
1637
         *
1638
         * Doing this get a bit tricky, as depending on flags, we may
1639
         * or may not flush all the entries in the slist.
1640
         *
1641
         * To make things more entertaining, with the advent of the
1642
         * fractal heap, the entry serialize callback can cause entries
1643
         * to be dirtied, resized, and/or moved.  Also, the
1644
         * pre_serialize callback can result in an entry being
1645
         * removed from the cache via the take ownership flag.
1646
         *
1647
         * To deal with this, we first make note of the initial
1648
         * skip list length and size:
1649
         */
1650
        initial_slist_len  = cache_ptr->slist_len;
1651
        initial_slist_size = cache_ptr->slist_size;
1652
1653
        /* As mentioned above, there is the possibility that
1654
         * entries will be dirtied, resized, flushed, or removed
1655
         * from the cache via the take ownership flag  during
1656
         * our pass through the skip list.  To capture the number
1657
         * of entries added, and the skip list size delta,
1658
         * zero the slist_len_increase and slist_size_increase of
1659
         * the cache's instance of H5C_t.  These fields will be
1660
         * updated elsewhere to account for slist insertions and/or
1661
         * dirty entry size changes.
1662
         */
1663
        cache_ptr->slist_len_increase  = 0;
1664
        cache_ptr->slist_size_increase = 0;
1665
1666
        /* at the end of the loop, use these values to compute the
1667
         * expected slist length and size and compare this with the
1668
         * value recorded in the cache's instance of H5C_t.
1669
         */
1670
#endif /* H5C_DO_SANITY_CHECKS */
1671
1672
6
        restart_slist_scan = true;
1673
12
        while ((restart_slist_scan) || (node_ptr != NULL)) {
1674
6
            if (restart_slist_scan) {
1675
6
                restart_slist_scan = false;
1676
1677
                /* Start at beginning of skip list */
1678
6
                node_ptr = H5SL_first(cache_ptr->slist_ptr);
1679
6
                if (node_ptr == NULL)
1680
                    /* the slist is empty -- break out of inner loop */
1681
0
                    break;
1682
1683
                /* Get cache entry for this node */
1684
6
                next_entry_ptr = (H5C_cache_entry_t *)H5SL_item(node_ptr);
1685
6
                if (NULL == next_entry_ptr)
1686
0
                    HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "next_entry_ptr == NULL ?!?!");
1687
1688
6
                assert(next_entry_ptr->is_dirty);
1689
6
                assert(next_entry_ptr->in_slist);
1690
6
            } /* end if */
1691
1692
6
            entry_ptr = next_entry_ptr;
1693
1694
            /* With the advent of the fractal heap, the free space
1695
             * manager, and the version 3 cache, it is possible
1696
             * that the pre-serialize or serialize callback will
1697
             * dirty, resize, or take ownership of other entries
1698
             * in the cache.
1699
             *
1700
             * To deal with this, there is code to detect any
1701
             * change in the skip list not directly under the control
1702
             * of this function.  If such modifications are detected,
1703
             * we must re-start the scan of the skip list to avoid
1704
             * the possibility that the target of the next_entry_ptr
1705
             * may have been flushed or deleted from the cache.
1706
             *
1707
             * To verify that all such possibilities have been dealt
1708
             * with, we do a bit of extra sanity checking on
1709
             * entry_ptr.
1710
             */
1711
6
            assert(entry_ptr->in_slist);
1712
6
            assert(entry_ptr->is_dirty);
1713
6
            assert(entry_ptr->ring >= ring);
1714
1715
            /* Advance node pointer now, before we delete its target
1716
             * from the slist.
1717
             */
1718
6
            node_ptr = H5SL_next(node_ptr);
1719
6
            if (node_ptr != NULL) {
1720
0
                next_entry_ptr = (H5C_cache_entry_t *)H5SL_item(node_ptr);
1721
0
                if (NULL == next_entry_ptr)
1722
0
                    HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "next_entry_ptr == NULL ?!?!");
1723
1724
0
                assert(next_entry_ptr->is_dirty);
1725
0
                assert(next_entry_ptr->in_slist);
1726
0
                assert(next_entry_ptr->ring >= ring);
1727
0
                assert(entry_ptr != next_entry_ptr);
1728
0
            } /* end if */
1729
6
            else
1730
6
                next_entry_ptr = NULL;
1731
1732
6
            if (((!entry_ptr->flush_me_last) ||
1733
6
                 ((entry_ptr->flush_me_last) && cache_ptr->num_last_entries >= cache_ptr->slist_len)) &&
1734
6
                ((entry_ptr->flush_dep_nchildren == 0) || (entry_ptr->flush_dep_ndirty_children == 0)) &&
1735
6
                (entry_ptr->ring == ring)) {
1736
1737
6
                assert(entry_ptr->flush_dep_nunser_children == 0);
1738
1739
6
                if (entry_ptr->is_protected) {
1740
                    /* we probably have major problems -- but lets
1741
                     * flush everything we can before we decide
1742
                     * whether to flag an error.
1743
                     */
1744
0
                    tried_to_flush_protected_entry = true;
1745
0
                    protected_entries++;
1746
0
                } /* end if */
1747
6
                else {
1748
6
                    if (H5C__flush_single_entry(f, entry_ptr, (flags | H5C__DURING_FLUSH_FLAG)) < 0)
1749
0
                        HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "Can't flush entry");
1750
1751
6
                    if (cache_ptr->slist_changed) {
1752
                        /* The slist has been modified by something
1753
                         * other than the simple removal of the
1754
                         * of the flushed entry after the flush.
1755
                         *
1756
                         * This has the potential to corrupt the
1757
                         * scan through the slist, so restart it.
1758
                         */
1759
0
                        restart_slist_scan       = true;
1760
0
                        cache_ptr->slist_changed = false;
1761
0
                        H5C__UPDATE_STATS_FOR_SLIST_SCAN_RESTART(cache_ptr);
1762
0
                    } /* end if */
1763
1764
6
                    flushed_entries_last_pass = true;
1765
6
                } /* end else */
1766
6
            }     /* end if */
1767
6
        }         /* while ( ( restart_slist_scan ) || ( node_ptr != NULL ) ) */
1768
1769
#ifdef H5C_DO_SANITY_CHECKS
1770
        /* Verify that the slist size and length are as expected. */
1771
        assert((uint32_t)((int32_t)initial_slist_len + cache_ptr->slist_len_increase) ==
1772
               cache_ptr->slist_len);
1773
        assert((size_t)((ssize_t)initial_slist_size + cache_ptr->slist_size_increase) ==
1774
               cache_ptr->slist_size);
1775
#endif /* H5C_DO_SANITY_CHECKS */
1776
6
    }  /* while */
1777
1778
120
    assert(protected_entries <= cache_ptr->pl_len);
1779
1780
120
    if (((cache_ptr->pl_len > 0) && !ignore_protected) || tried_to_flush_protected_entry)
1781
0
        HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "cache has protected items");
1782
1783
#ifdef H5C_DO_SANITY_CHECKS
1784
    assert(cache_ptr->slist_ring_len[ring] == 0);
1785
    assert(cache_ptr->slist_ring_size[ring] == 0);
1786
#endif /* H5C_DO_SANITY_CHECKS */
1787
1788
120
done:
1789
120
    FUNC_LEAVE_NOAPI(ret_value)
1790
120
} /* H5C__flush_ring() */
1791
1792
/*-------------------------------------------------------------------------
1793
 * Function:    H5C__make_space_in_cache
1794
 *
1795
 * Purpose:     Attempt to evict cache entries until the index_size
1796
 *        is at least needed_space below max_cache_size.
1797
 *
1798
 *        In passing, also attempt to bring cLRU_list_size to a
1799
 *        value greater than min_clean_size.
1800
 *
1801
 *        Depending on circumstances, both of these goals may
1802
 *        be impossible, as in parallel mode, we must avoid generating
1803
 *        a write as part of a read (to avoid deadlock in collective
1804
 *        I/O), and in all cases, it is possible (though hopefully
1805
 *        highly unlikely) that the protected list may exceed the
1806
 *        maximum size of the cache.
1807
 *
1808
 *        Thus the function simply does its best, returning success
1809
 *        unless an error is encountered.
1810
 *
1811
 *        Observe that this function cannot occasion a read.
1812
 *
1813
 * Return:      Non-negative on success/Negative on failure.
1814
 *
1815
 *-------------------------------------------------------------------------
1816
 */
1817
herr_t
1818
H5C__make_space_in_cache(H5F_t *f, size_t space_needed, bool write_permitted)
1819
12
{
1820
12
    H5C_t *cache_ptr = f->shared->cache;
1821
#if H5C_COLLECT_CACHE_STATS
1822
    int32_t clean_entries_skipped    = 0;
1823
    int32_t dirty_pf_entries_skipped = 0;
1824
    int32_t total_entries_scanned    = 0;
1825
#endif /* H5C_COLLECT_CACHE_STATS */
1826
12
    uint32_t           entries_examined = 0;
1827
12
    uint32_t           initial_list_len;
1828
12
    size_t             empty_space;
1829
12
    bool               reentrant_call    = false;
1830
12
    bool               prev_is_dirty     = false;
1831
12
    bool               didnt_flush_entry = false;
1832
12
    bool               restart_scan;
1833
12
    H5C_cache_entry_t *entry_ptr;
1834
12
    H5C_cache_entry_t *prev_ptr;
1835
12
    H5C_cache_entry_t *next_ptr;
1836
#ifndef NDEBUG
1837
    uint32_t num_corked_entries = 0;
1838
#endif
1839
12
    herr_t ret_value = SUCCEED; /* Return value */
1840
1841
12
    FUNC_ENTER_PACKAGE
1842
1843
    /* Sanity checks */
1844
12
    assert(f);
1845
12
    assert(cache_ptr);
1846
12
    assert(cache_ptr->index_size == (cache_ptr->clean_index_size + cache_ptr->dirty_index_size));
1847
1848
    /* check to see if cache_ptr->msic_in_progress is true.  If it, this
1849
     * is a re-entrant call via a client callback called in the make
1850
     * space in cache process.  To avoid an infinite recursion, set
1851
     * reentrant_call to true, and goto done.
1852
     */
1853
12
    if (cache_ptr->msic_in_progress) {
1854
0
        reentrant_call = true;
1855
0
        HGOTO_DONE(SUCCEED);
1856
0
    } /* end if */
1857
1858
12
    cache_ptr->msic_in_progress = true;
1859
1860
12
    if (write_permitted) {
1861
12
        restart_scan     = false;
1862
12
        initial_list_len = cache_ptr->LRU_list_len;
1863
12
        entry_ptr        = cache_ptr->LRU_tail_ptr;
1864
1865
12
        if (cache_ptr->index_size >= cache_ptr->max_cache_size)
1866
6
            empty_space = 0;
1867
6
        else
1868
6
            empty_space = cache_ptr->max_cache_size - cache_ptr->index_size;
1869
1870
12
        while ((((cache_ptr->index_size + space_needed) > cache_ptr->max_cache_size) ||
1871
0
                ((empty_space + cache_ptr->clean_index_size) < (cache_ptr->min_clean_size))) &&
1872
12
               (entries_examined <= (2 * initial_list_len)) && (entry_ptr != NULL)) {
1873
0
            assert(!(entry_ptr->is_protected));
1874
0
            assert(!(entry_ptr->is_read_only));
1875
0
            assert((entry_ptr->ro_ref_count) == 0);
1876
1877
0
            next_ptr = entry_ptr->next;
1878
0
            prev_ptr = entry_ptr->prev;
1879
1880
0
            if (prev_ptr != NULL)
1881
0
                prev_is_dirty = prev_ptr->is_dirty;
1882
1883
0
            if (entry_ptr->is_dirty && (entry_ptr->tag_info && entry_ptr->tag_info->corked)) {
1884
                /* Skip "dirty" corked entries.  */
1885
#ifndef NDEBUG
1886
                ++num_corked_entries;
1887
#endif
1888
0
                didnt_flush_entry = true;
1889
0
            }
1890
0
            else if ((entry_ptr->type->id != H5AC_EPOCH_MARKER_ID) && !entry_ptr->flush_in_progress &&
1891
0
                     !entry_ptr->prefetched_dirty) {
1892
0
                didnt_flush_entry = false;
1893
0
                if (entry_ptr->is_dirty) {
1894
#if H5C_COLLECT_CACHE_STATS
1895
                    if ((cache_ptr->index_size + space_needed) > cache_ptr->max_cache_size)
1896
                        cache_ptr->entries_scanned_to_make_space++;
1897
#endif /* H5C_COLLECT_CACHE_STATS */
1898
1899
                    /* reset entries_removed_counter and
1900
                     * last_entry_removed_ptr prior to the call to
1901
                     * H5C__flush_single_entry() so that we can spot
1902
                     * unexpected removals of entries from the cache,
1903
                     * and set the restart_scan flag if proceeding
1904
                     * would be likely to cause us to scan an entry
1905
                     * that is no longer in the cache.
1906
                     */
1907
0
                    cache_ptr->entries_removed_counter = 0;
1908
0
                    cache_ptr->last_entry_removed_ptr  = NULL;
1909
1910
0
                    if (H5C__flush_single_entry(f, entry_ptr, H5C__NO_FLAGS_SET) < 0)
1911
0
                        HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to flush entry");
1912
1913
0
                    if ((cache_ptr->entries_removed_counter > 1) ||
1914
0
                        (cache_ptr->last_entry_removed_ptr == prev_ptr))
1915
1916
0
                        restart_scan = true;
1917
0
                }
1918
0
                else if ((cache_ptr->index_size + space_needed) > cache_ptr->max_cache_size
1919
#ifdef H5_HAVE_PARALLEL
1920
                         && !(entry_ptr->coll_access)
1921
#endif /* H5_HAVE_PARALLEL */
1922
0
                ) {
1923
#if H5C_COLLECT_CACHE_STATS
1924
                    cache_ptr->entries_scanned_to_make_space++;
1925
#endif /* H5C_COLLECT_CACHE_STATS */
1926
1927
0
                    if (H5C__flush_single_entry(f, entry_ptr,
1928
0
                                                H5C__FLUSH_INVALIDATE_FLAG |
1929
0
                                                    H5C__DEL_FROM_SLIST_ON_DESTROY_FLAG) < 0)
1930
0
                        HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to flush entry");
1931
0
                }
1932
0
                else {
1933
                    /* We have enough space so don't flush clean entry. */
1934
#if H5C_COLLECT_CACHE_STATS
1935
                    clean_entries_skipped++;
1936
#endif /* H5C_COLLECT_CACHE_STATS */
1937
0
                    didnt_flush_entry = true;
1938
0
                }
1939
1940
#if H5C_COLLECT_CACHE_STATS
1941
                total_entries_scanned++;
1942
#endif /* H5C_COLLECT_CACHE_STATS */
1943
0
            }
1944
0
            else {
1945
1946
                /* Skip epoch markers, entries that are in the process
1947
                 * of being flushed, and entries marked as prefetched_dirty
1948
                 * (occurs in the R/O case only).
1949
                 */
1950
0
                didnt_flush_entry = true;
1951
1952
#if H5C_COLLECT_CACHE_STATS
1953
                if (entry_ptr->prefetched_dirty)
1954
                    dirty_pf_entries_skipped++;
1955
#endif /* H5C_COLLECT_CACHE_STATS */
1956
0
            }
1957
1958
0
            if (prev_ptr != NULL) {
1959
0
                if (didnt_flush_entry)
1960
                    /* epoch markers don't get flushed, and we don't touch
1961
                     * entries that are in the process of being flushed.
1962
                     * Hence no need for sanity checks, as we haven't
1963
                     * flushed anything.  Thus just set entry_ptr to prev_ptr
1964
                     * and go on.
1965
                     */
1966
0
                    entry_ptr = prev_ptr;
1967
0
                else if (restart_scan || prev_ptr->is_dirty != prev_is_dirty || prev_ptr->next != next_ptr ||
1968
0
                         prev_ptr->is_protected || prev_ptr->is_pinned) {
1969
                    /* something has happened to the LRU -- start over
1970
                     * from the tail.
1971
                     */
1972
0
                    restart_scan = false;
1973
0
                    entry_ptr    = cache_ptr->LRU_tail_ptr;
1974
0
                    H5C__UPDATE_STATS_FOR_LRU_SCAN_RESTART(cache_ptr);
1975
0
                }
1976
0
                else
1977
0
                    entry_ptr = prev_ptr;
1978
0
            }
1979
0
            else
1980
0
                entry_ptr = NULL;
1981
1982
0
            entries_examined++;
1983
1984
0
            if (cache_ptr->index_size >= cache_ptr->max_cache_size)
1985
0
                empty_space = 0;
1986
0
            else
1987
0
                empty_space = cache_ptr->max_cache_size - cache_ptr->index_size;
1988
1989
0
            assert(cache_ptr->index_size == (cache_ptr->clean_index_size + cache_ptr->dirty_index_size));
1990
0
        }
1991
1992
#if H5C_COLLECT_CACHE_STATS
1993
        cache_ptr->calls_to_msic++;
1994
1995
        cache_ptr->total_entries_skipped_in_msic += clean_entries_skipped;
1996
        cache_ptr->total_dirty_pf_entries_skipped_in_msic += dirty_pf_entries_skipped;
1997
        cache_ptr->total_entries_scanned_in_msic += total_entries_scanned;
1998
1999
        if (clean_entries_skipped > cache_ptr->max_entries_skipped_in_msic)
2000
            cache_ptr->max_entries_skipped_in_msic = clean_entries_skipped;
2001
2002
        if (dirty_pf_entries_skipped > cache_ptr->max_dirty_pf_entries_skipped_in_msic)
2003
            cache_ptr->max_dirty_pf_entries_skipped_in_msic = dirty_pf_entries_skipped;
2004
2005
        if (total_entries_scanned > cache_ptr->max_entries_scanned_in_msic)
2006
            cache_ptr->max_entries_scanned_in_msic = total_entries_scanned;
2007
#endif /* H5C_COLLECT_CACHE_STATS */
2008
2009
        /* NEED: work on a better assert for corked entries */
2010
12
        assert((entries_examined > (2 * initial_list_len)) ||
2011
12
               ((cache_ptr->pl_size + cache_ptr->pel_size + cache_ptr->min_clean_size) >
2012
12
                cache_ptr->max_cache_size) ||
2013
12
               ((cache_ptr->clean_index_size + empty_space) >= cache_ptr->min_clean_size) ||
2014
12
               ((num_corked_entries)));
2015
#if H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS
2016
2017
        assert((entries_examined > (2 * initial_list_len)) ||
2018
               (cache_ptr->cLRU_list_size <= cache_ptr->clean_index_size));
2019
        assert((entries_examined > (2 * initial_list_len)) ||
2020
               (cache_ptr->dLRU_list_size <= cache_ptr->dirty_index_size));
2021
2022
#endif /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */
2023
12
    }
2024
0
    else {
2025
0
        assert(H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS);
2026
2027
#if H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS
2028
        initial_list_len = cache_ptr->cLRU_list_len;
2029
        entry_ptr        = cache_ptr->cLRU_tail_ptr;
2030
2031
        while (((cache_ptr->index_size + space_needed) > cache_ptr->max_cache_size) &&
2032
               (entries_examined <= initial_list_len) && (entry_ptr != NULL)) {
2033
            assert(!(entry_ptr->is_protected));
2034
            assert(!(entry_ptr->is_read_only));
2035
            assert((entry_ptr->ro_ref_count) == 0);
2036
            assert(!(entry_ptr->is_dirty));
2037
2038
            prev_ptr = entry_ptr->aux_prev;
2039
2040
            if (!entry_ptr->prefetched_dirty
2041
#ifdef H5_HAVE_PARALLEL
2042
                && !entry_ptr->coll_access
2043
#endif /* H5_HAVE_PARALLEL */
2044
            ) {
2045
                if (H5C__flush_single_entry(
2046
                        f, entry_ptr, H5C__FLUSH_INVALIDATE_FLAG | H5C__DEL_FROM_SLIST_ON_DESTROY_FLAG) < 0)
2047
                    HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to flush entry");
2048
            } /* end if */
2049
2050
            /* we are scanning the clean LRU, so the serialize function
2051
             * will not be called on any entry -- thus there is no
2052
             * concern about the list being modified out from under
2053
             * this function.
2054
             */
2055
2056
            entry_ptr = prev_ptr;
2057
            entries_examined++;
2058
        }
2059
#endif /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */
2060
0
    }
2061
2062
12
done:
2063
    /* Sanity checks */
2064
12
    assert(cache_ptr->msic_in_progress);
2065
12
    if (!reentrant_call)
2066
12
        cache_ptr->msic_in_progress = false;
2067
12
    assert((!reentrant_call) || (cache_ptr->msic_in_progress));
2068
2069
12
    FUNC_LEAVE_NOAPI(ret_value)
2070
12
} /* H5C__make_space_in_cache() */
2071
2072
/*-------------------------------------------------------------------------
2073
 * Function:    H5C__serialize_cache
2074
 *
2075
 * Purpose:    Serialize (i.e. construct an on disk image) for all entries
2076
 *        in the metadata cache including clean entries.
2077
 *
2078
 *        Note that flush dependencies and "flush me last" flags
2079
 *        must be observed in the serialization process.
2080
 *
2081
 *        Note also that entries may be loaded, flushed, evicted,
2082
 *        expunged, relocated, resized, or removed from the cache
2083
 *        during this process, just as these actions may occur during
2084
 *        a regular flush.
2085
 *
2086
 *        However, we are given that the cache will contain no protected
2087
 *        entries on entry to this routine (although entries may be
2088
 *        briefly protected and then unprotected during the serialize
2089
 *        process).
2090
 *
2091
 *        The objective of this routine is serialize all entries and
2092
 *        to force all entries into their actual locations on disk.
2093
 *
2094
 *        The initial need for this routine is to settle all entries
2095
 *        in the cache prior to construction of the metadata cache
2096
 *        image so that the size of the cache image can be calculated.
2097
 *
2098
 * Return:      Non-negative on success/Negative on failure or if there was
2099
 *        a request to flush all items and something was protected.
2100
 *
2101
 *-------------------------------------------------------------------------
2102
 */
2103
herr_t
2104
H5C__serialize_cache(H5F_t *f)
2105
0
{
2106
#ifdef H5C_DO_SANITY_CHECKS
2107
    int      i;
2108
    uint32_t index_len        = 0;
2109
    size_t   index_size       = (size_t)0;
2110
    size_t   clean_index_size = (size_t)0;
2111
    size_t   dirty_index_size = (size_t)0;
2112
    size_t   slist_size       = (size_t)0;
2113
    uint32_t slist_len        = 0;
2114
#endif /* H5C_DO_SANITY_CHECKS */
2115
0
    H5C_ring_t ring;
2116
0
    H5C_t     *cache_ptr;
2117
0
    herr_t     ret_value = SUCCEED;
2118
2119
0
    FUNC_ENTER_PACKAGE
2120
2121
    /* Sanity checks */
2122
0
    assert(f);
2123
0
    assert(f->shared);
2124
0
    cache_ptr = f->shared->cache;
2125
0
    assert(cache_ptr);
2126
0
    assert(cache_ptr->slist_ptr);
2127
2128
#ifdef H5C_DO_SANITY_CHECKS
2129
    assert(cache_ptr->index_ring_len[H5C_RING_UNDEFINED] == 0);
2130
    assert(cache_ptr->index_ring_size[H5C_RING_UNDEFINED] == (size_t)0);
2131
    assert(cache_ptr->clean_index_ring_size[H5C_RING_UNDEFINED] == (size_t)0);
2132
    assert(cache_ptr->dirty_index_ring_size[H5C_RING_UNDEFINED] == (size_t)0);
2133
    assert(cache_ptr->slist_ring_len[H5C_RING_UNDEFINED] == 0);
2134
    assert(cache_ptr->slist_ring_size[H5C_RING_UNDEFINED] == (size_t)0);
2135
2136
    for (i = H5C_RING_USER; i < H5C_RING_NTYPES; i++) {
2137
        index_len += cache_ptr->index_ring_len[i];
2138
        index_size += cache_ptr->index_ring_size[i];
2139
        clean_index_size += cache_ptr->clean_index_ring_size[i];
2140
        dirty_index_size += cache_ptr->dirty_index_ring_size[i];
2141
2142
        slist_len += cache_ptr->slist_ring_len[i];
2143
        slist_size += cache_ptr->slist_ring_size[i];
2144
    } /* end for */
2145
2146
    assert(cache_ptr->index_len == index_len);
2147
    assert(cache_ptr->index_size == index_size);
2148
    assert(cache_ptr->clean_index_size == clean_index_size);
2149
    assert(cache_ptr->dirty_index_size == dirty_index_size);
2150
    assert(cache_ptr->slist_len == slist_len);
2151
    assert(cache_ptr->slist_size == slist_size);
2152
#endif /* H5C_DO_SANITY_CHECKS */
2153
2154
#ifdef H5C_DO_EXTREME_SANITY_CHECKS
2155
    if (H5C__validate_protected_entry_list(cache_ptr) < 0 || H5C__validate_pinned_entry_list(cache_ptr) < 0 ||
2156
        H5C__validate_lru_list(cache_ptr) < 0)
2157
        HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on entry");
2158
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
2159
2160
#ifndef NDEBUG
2161
    /* if this is a debug build, set the serialization_count field of
2162
     * each entry in the cache to zero before we start the serialization.
2163
     * This allows us to detect the case in which any entry is serialized
2164
     * more than once (a performance issues), and more importantly, the
2165
     * case is which any flush dependency parent is serializes more than
2166
     * once (a correctness issue).
2167
     */
2168
    {
2169
        H5C_cache_entry_t *scan_ptr = NULL;
2170
2171
        scan_ptr = cache_ptr->il_head;
2172
        while (scan_ptr != NULL) {
2173
            scan_ptr->serialization_count = 0;
2174
            scan_ptr                      = scan_ptr->il_next;
2175
        } /* end while */
2176
    }     /* end block */
2177
#endif
2178
2179
    /* set cache_ptr->serialization_in_progress to true, and back
2180
     * to false at the end of the function.  Must maintain this flag
2181
     * to support H5C_get_serialization_in_progress(), which is in
2182
     * turn required to support sanity checking in some cache
2183
     * clients.
2184
     */
2185
0
    assert(!cache_ptr->serialization_in_progress);
2186
0
    cache_ptr->serialization_in_progress = true;
2187
2188
    /* Serialize each ring, starting from the outermost ring and
2189
     * working inward.
2190
     */
2191
0
    ring = H5C_RING_USER;
2192
0
    while (ring < H5C_RING_NTYPES) {
2193
0
        assert(cache_ptr->close_warning_received);
2194
0
        switch (ring) {
2195
0
            case H5C_RING_USER:
2196
0
                break;
2197
2198
0
            case H5C_RING_RDFSM:
2199
                /* Settle raw data FSM */
2200
0
                if (!cache_ptr->rdfsm_settled)
2201
0
                    if (H5MF_settle_raw_data_fsm(f, &cache_ptr->rdfsm_settled) < 0)
2202
0
                        HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "RD FSM settle failed");
2203
0
                break;
2204
2205
0
            case H5C_RING_MDFSM:
2206
                /* Settle metadata FSM */
2207
0
                if (!cache_ptr->mdfsm_settled)
2208
0
                    if (H5MF_settle_meta_data_fsm(f, &cache_ptr->mdfsm_settled) < 0)
2209
0
                        HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "MD FSM settle failed");
2210
0
                break;
2211
2212
0
            case H5C_RING_SBE:
2213
0
            case H5C_RING_SB:
2214
0
                break;
2215
2216
0
            default:
2217
0
                HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Unknown ring?!?!");
2218
0
                break;
2219
0
        } /* end switch */
2220
2221
0
        if (H5C__serialize_ring(f, ring) < 0)
2222
0
            HGOTO_ERROR(H5E_CACHE, H5E_CANTSERIALIZE, FAIL, "serialize ring failed");
2223
2224
0
        ring++;
2225
0
    } /* end while */
2226
2227
#ifndef NDEBUG
2228
    /* Verify that no entry has been serialized more than once.
2229
     * FD parents with multiple serializations should have been caught
2230
     * elsewhere, so no specific check for them here.
2231
     */
2232
    {
2233
        H5C_cache_entry_t *scan_ptr = NULL;
2234
2235
        scan_ptr = cache_ptr->il_head;
2236
        while (scan_ptr != NULL) {
2237
            assert(scan_ptr->serialization_count <= 1);
2238
2239
            scan_ptr = scan_ptr->il_next;
2240
        } /* end while */
2241
    }     /* end block */
2242
#endif
2243
2244
0
done:
2245
0
    cache_ptr->serialization_in_progress = false;
2246
0
    FUNC_LEAVE_NOAPI(ret_value)
2247
0
} /* H5C__serialize_cache() */
2248
2249
/*-------------------------------------------------------------------------
2250
 * Function:    H5C__serialize_ring
2251
 *
2252
 * Purpose:     Serialize the entries contained in the specified cache and
2253
 *              ring.  All entries in rings outside the specified ring
2254
 *              must have been serialized on entry.
2255
 *
2256
 *              If the cache contains protected entries in the specified
2257
 *              ring, the function will fail, as protected entries cannot
2258
 *              be serialized.  However all unprotected entries in the
2259
 *              target ring should be serialized before the function
2260
 *              returns failure.
2261
 *
2262
 *              If flush dependencies appear in the target ring, the
2263
 *              function makes repeated passes through the index list
2264
 *              serializing entries in flush dependency order.
2265
 *
2266
 *              All entries outside the H5C_RING_SBE are marked for
2267
 *              inclusion in the cache image.  Entries in H5C_RING_SBE
2268
 *              and below are marked for exclusion from the image.
2269
 *
2270
 * Return:      Non-negative on success/Negative on failure or if there was
2271
 *              a request to flush all items and something was protected.
2272
 *
2273
 *-------------------------------------------------------------------------
2274
 */
2275
static herr_t
2276
H5C__serialize_ring(H5F_t *f, H5C_ring_t ring)
2277
0
{
2278
0
    bool               done = false;
2279
0
    H5C_t             *cache_ptr;
2280
0
    H5C_cache_entry_t *entry_ptr;
2281
0
    herr_t             ret_value = SUCCEED;
2282
2283
0
    FUNC_ENTER_PACKAGE
2284
2285
    /* Sanity checks */
2286
0
    assert(f);
2287
0
    assert(f->shared);
2288
0
    cache_ptr = f->shared->cache;
2289
0
    assert(cache_ptr);
2290
0
    assert(ring > H5C_RING_UNDEFINED);
2291
0
    assert(ring < H5C_RING_NTYPES);
2292
2293
0
    assert(cache_ptr->serialization_in_progress);
2294
2295
    /* The objective here is to serialize all entries in the cache ring
2296
     * in flush dependency order.
2297
     *
2298
     * The basic algorithm is to scan the cache index list looking for
2299
     * unserialized entries that are either not in a flush dependency
2300
     * relationship, or which have no unserialized children.  Any such
2301
     * entry is serialized and its flush dependency parents (if any) are
2302
     * informed -- allowing them to decrement their userialized child counts.
2303
     *
2304
     * However, this algorithm is complicated by the ability
2305
     * of client serialization callbacks to perform operations on
2306
     * on the cache which can result in the insertion, deletion,
2307
     * relocation, resize, dirty, flush, eviction, or removal (via the
2308
     * take ownership flag) of entries.  Changes in the flush dependency
2309
     * structure are also possible.
2310
     *
2311
     * On the other hand, the algorithm is simplified by the fact that
2312
     * we are serializing, not flushing.  Thus, as long as all entries
2313
     * are serialized correctly, it doesn't matter if we have to go back
2314
     * and serialize an entry a second time.
2315
     *
2316
     * These possible actions result in the following modifications to
2317
     * the basic algorithm:
2318
     *
2319
     * 1) In the event of an entry expunge, eviction or removal, we must
2320
     *    restart the scan as it is possible that the next entry in our
2321
     *    scan is no longer in the cache.  Were we to examine this entry,
2322
     *    we would be accessing deallocated memory.
2323
     *
2324
     * 2) A resize, dirty, or insertion of an entry may result in the
2325
     *    the increment of a flush dependency parent's dirty and/or
2326
     *    unserialized child count.  In the context of serializing the
2327
     *    the cache, this is a non-issue, as even if we have already
2328
     *    serialized the parent, it will be marked dirty and its image
2329
     *    marked out of date if appropriate when the child is serialized.
2330
     *
2331
     *    However, this is a major issue for a flush, as were this to happen
2332
     *    in a flush, it would violate the invariant that the flush dependency
2333
     *    feature is intended to enforce.  As the metadata cache has no
2334
     *    control over the behavior of cache clients, it has no way of
2335
     *    preventing this behaviour.  However, it should detect it if at all
2336
     *    possible.
2337
     *
2338
     *    Do this by maintaining a count of the number of times each entry is
2339
     *    serialized during a cache serialization.  If any flush dependency
2340
     *    parent is serialized more than once, throw an assertion failure.
2341
     *
2342
     * 3) An entry relocation will typically change the location of the
2343
     *    entry in the index list.  This shouldn't cause problems as we
2344
     *    will scan the index list until we make a complete pass without
2345
     *    finding anything to serialize -- making relocations of either
2346
     *    the current or next entries irrelevant.
2347
     *
2348
     *    Note that since a relocation may result in our skipping part of
2349
     *    the index list, we must always do at least one more pass through
2350
     *    the index list after an entry relocation.
2351
     *
2352
     * 4) Changes in the flush dependency structure are possible on
2353
     *    entry insertion, load, expunge, evict, or remove.  Destruction
2354
     *    of a flush dependency has no effect, as it can only relax the
2355
     *    flush dependencies.  Creation of a flush dependency can create
2356
     *    an unserialized child of a flush dependency parent where all
2357
     *    flush dependency children were previously serialized.  Should
2358
     *    this child dirty the flush dependency parent when it is serialized,
2359
     *    the parent will be re-serialized.
2360
     *
2361
     *    Per the discussion of 2) above, this is a non issue for cache
2362
     *    serialization, and a major problem for cache flush.  Using the
2363
     *    same detection mechanism, throw an assertion failure if this
2364
     *    condition appears.
2365
     *
2366
     * Observe that either eviction or removal of entries as a result of
2367
     * a serialization is not a problem as long as the flush dependency
2368
     * tree does not change beyond the removal of a leaf.
2369
     */
2370
0
    while (!done) {
2371
        /* Reset the counters so that we can detect insertions, loads,
2372
         * moves, and flush dependency height changes caused by the pre_serialize
2373
         * and serialize callbacks.
2374
         */
2375
0
        cache_ptr->entries_loaded_counter    = 0;
2376
0
        cache_ptr->entries_inserted_counter  = 0;
2377
0
        cache_ptr->entries_relocated_counter = 0;
2378
2379
0
        done      = true; /* set to false if any activity in inner loop */
2380
0
        entry_ptr = cache_ptr->il_head;
2381
0
        while (entry_ptr != NULL) {
2382
            /* Verify that either the entry is already serialized, or
2383
             * that it is assigned to either the target or an inner
2384
             * ring.
2385
             */
2386
0
            assert((entry_ptr->ring >= ring) || (entry_ptr->image_up_to_date));
2387
2388
            /* Skip flush me last entries or inner ring entries */
2389
0
            if (!entry_ptr->flush_me_last && entry_ptr->ring == ring) {
2390
2391
                /* if we encounter an unserialized entry in the current
2392
                 * ring that is not marked flush me last, we are not done.
2393
                 */
2394
0
                if (!entry_ptr->image_up_to_date)
2395
0
                    done = false;
2396
2397
                /* Serialize the entry if its image is not up to date
2398
                 * and it has no unserialized flush dependency children.
2399
                 */
2400
0
                if (!entry_ptr->image_up_to_date && entry_ptr->flush_dep_nunser_children == 0) {
2401
0
                    assert(entry_ptr->serialization_count == 0);
2402
2403
                    /* Serialize the entry */
2404
0
                    if (H5C__serialize_single_entry(f, cache_ptr, entry_ptr) < 0)
2405
0
                        HGOTO_ERROR(H5E_CACHE, H5E_CANTSERIALIZE, FAIL, "entry serialization failed");
2406
2407
0
                    assert(entry_ptr->flush_dep_nunser_children == 0);
2408
0
                    assert(entry_ptr->serialization_count == 0);
2409
2410
#ifndef NDEBUG
2411
                    /* Increment serialization counter (to detect multiple serializations) */
2412
                    entry_ptr->serialization_count++;
2413
#endif
2414
0
                } /* end if */
2415
0
            }     /* end if */
2416
2417
            /* Check for the cache being perturbed during the entry serialize */
2418
0
            if ((cache_ptr->entries_loaded_counter > 0) || (cache_ptr->entries_inserted_counter > 0) ||
2419
0
                (cache_ptr->entries_relocated_counter > 0)) {
2420
2421
#if H5C_COLLECT_CACHE_STATS
2422
                H5C__UPDATE_STATS_FOR_INDEX_SCAN_RESTART(cache_ptr);
2423
#endif /* H5C_COLLECT_CACHE_STATS */
2424
2425
                /* Reset the counters */
2426
0
                cache_ptr->entries_loaded_counter    = 0;
2427
0
                cache_ptr->entries_inserted_counter  = 0;
2428
0
                cache_ptr->entries_relocated_counter = 0;
2429
2430
                /* Restart scan */
2431
0
                entry_ptr = cache_ptr->il_head;
2432
0
            } /* end if */
2433
0
            else
2434
                /* Advance to next entry */
2435
0
                entry_ptr = entry_ptr->il_next;
2436
0
        } /* while ( entry_ptr != NULL ) */
2437
0
    }     /* while ( ! done ) */
2438
2439
    /* Reset the counters so that we can detect insertions, loads,
2440
     * moves, and flush dependency height changes caused by the pre_serialize
2441
     * and serialize callbacks.
2442
     */
2443
0
    cache_ptr->entries_loaded_counter    = 0;
2444
0
    cache_ptr->entries_inserted_counter  = 0;
2445
0
    cache_ptr->entries_relocated_counter = 0;
2446
2447
    /* At this point, all entries not marked "flush me last" and in
2448
     * the current ring or outside it should be serialized and have up
2449
     * to date images.  Scan the index list again to serialize the
2450
     * "flush me last" entries (if they are in the current ring) and to
2451
     * verify that all other entries have up to date images.
2452
     */
2453
0
    entry_ptr = cache_ptr->il_head;
2454
0
    while (entry_ptr != NULL) {
2455
0
        assert(entry_ptr->ring > H5C_RING_UNDEFINED);
2456
0
        assert(entry_ptr->ring < H5C_RING_NTYPES);
2457
0
        assert((entry_ptr->ring >= ring) || (entry_ptr->image_up_to_date));
2458
2459
0
        if (entry_ptr->ring == ring) {
2460
0
            if (entry_ptr->flush_me_last) {
2461
0
                if (!entry_ptr->image_up_to_date) {
2462
0
                    assert(entry_ptr->serialization_count == 0);
2463
0
                    assert(entry_ptr->flush_dep_nunser_children == 0);
2464
2465
                    /* Serialize the entry */
2466
0
                    if (H5C__serialize_single_entry(f, cache_ptr, entry_ptr) < 0)
2467
0
                        HGOTO_ERROR(H5E_CACHE, H5E_CANTSERIALIZE, FAIL, "entry serialization failed");
2468
2469
                    /* Check for the cache changing */
2470
0
                    if ((cache_ptr->entries_loaded_counter > 0) ||
2471
0
                        (cache_ptr->entries_inserted_counter > 0) ||
2472
0
                        (cache_ptr->entries_relocated_counter > 0))
2473
0
                        HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL,
2474
0
                                    "flush_me_last entry serialization triggered restart");
2475
2476
0
                    assert(entry_ptr->flush_dep_nunser_children == 0);
2477
0
                    assert(entry_ptr->serialization_count == 0);
2478
#ifndef NDEBUG
2479
                    /* Increment serialization counter (to detect multiple serializations) */
2480
                    entry_ptr->serialization_count++;
2481
#endif
2482
0
                } /* end if */
2483
0
            }     /* end if */
2484
0
            else {
2485
0
                assert(entry_ptr->image_up_to_date);
2486
0
                assert(entry_ptr->serialization_count <= 1);
2487
0
                assert(entry_ptr->flush_dep_nunser_children == 0);
2488
0
            } /* end else */
2489
0
        }     /* if ( entry_ptr->ring == ring ) */
2490
2491
0
        entry_ptr = entry_ptr->il_next;
2492
0
    } /* while ( entry_ptr != NULL ) */
2493
2494
0
done:
2495
0
    assert(cache_ptr->serialization_in_progress);
2496
0
    FUNC_LEAVE_NOAPI(ret_value)
2497
0
} /* H5C__serialize_ring() */