Coverage Report

Created: 2024-06-18 06:29

/src/hdf5/src/H5HFcache.c
Line
Count
Source (jump to first uncovered line)
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 COPYING 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:   H5HFcache.c
16
 *
17
 * Purpose:   Implement fractal heap metadata cache methods
18
 *
19
 *-------------------------------------------------------------------------
20
 */
21
22
/****************/
23
/* Module Setup */
24
/****************/
25
26
#include "H5HFmodule.h" /* This source code file is part of the H5HF module */
27
28
/***********/
29
/* Headers */
30
/***********/
31
#include "H5private.h"   /* Generic Functions     */
32
#include "H5ACprivate.h" /* Metadata cache      */
33
#include "H5Eprivate.h"  /* Error handling        */
34
#include "H5FLprivate.h" /* Free Lists                               */
35
#include "H5HFpkg.h"     /* Fractal heaps     */
36
#include "H5MFprivate.h" /* File memory management    */
37
#include "H5MMprivate.h" /* Memory management     */
38
39
/****************/
40
/* Local Macros */
41
/****************/
42
43
/* Fractal heap format version #'s */
44
0
#define H5HF_HDR_VERSION    0 /* Header */
45
0
#define H5HF_DBLOCK_VERSION 0 /* Direct block */
46
0
#define H5HF_IBLOCK_VERSION 0 /* Indirect block */
47
48
/******************/
49
/* Local Typedefs */
50
/******************/
51
52
/********************/
53
/* Package Typedefs */
54
/********************/
55
56
/********************/
57
/* Local Prototypes */
58
/********************/
59
60
/* Local encode/decode routines */
61
static herr_t H5HF__hdr_prefix_decode(H5HF_hdr_t *hdr, const uint8_t **image_ref);
62
static herr_t H5HF__dtable_encode(H5F_t *f, uint8_t **pp, const H5HF_dtable_t *dtable);
63
static herr_t H5HF__dtable_decode(H5F_t *f, const uint8_t **pp, H5HF_dtable_t *dtable);
64
65
/* Metadata cache (H5AC) callbacks */
66
static herr_t H5HF__cache_hdr_get_initial_load_size(void *udata, size_t *image_len);
67
static herr_t H5HF__cache_hdr_get_final_load_size(const void *image_ptr, size_t image_len, void *udata,
68
                                                  size_t *actual_len);
69
static htri_t H5HF__cache_hdr_verify_chksum(const void *image_ptr, size_t len, void *udata_ptr);
70
static void  *H5HF__cache_hdr_deserialize(const void *image, size_t len, void *udata, bool *dirty);
71
static herr_t H5HF__cache_hdr_image_len(const void *thing, size_t *image_len);
72
static herr_t H5HF__cache_hdr_pre_serialize(H5F_t *f, void *thing, haddr_t addr, size_t len,
73
                                            haddr_t *new_addr, size_t *new_len, unsigned *flags);
74
static herr_t H5HF__cache_hdr_serialize(const H5F_t *f, void *image, size_t len, void *thing);
75
static herr_t H5HF__cache_hdr_free_icr(void *thing);
76
77
static herr_t H5HF__cache_iblock_get_initial_load_size(void *udata, size_t *image_len);
78
static htri_t H5HF__cache_iblock_verify_chksum(const void *image_ptr, size_t len, void *udata_ptr);
79
static void  *H5HF__cache_iblock_deserialize(const void *image, size_t len, void *udata, bool *dirty);
80
static herr_t H5HF__cache_iblock_image_len(const void *thing, size_t *image_len);
81
static herr_t H5HF__cache_iblock_pre_serialize(H5F_t *f, void *thing, haddr_t addr, size_t len,
82
                                               haddr_t *new_addr, size_t *new_len, unsigned *flags);
83
static herr_t H5HF__cache_iblock_serialize(const H5F_t *f, void *image, size_t len, void *thing);
84
static herr_t H5HF__cache_iblock_notify(H5AC_notify_action_t action, void *thing);
85
static herr_t H5HF__cache_iblock_free_icr(void *thing);
86
87
static herr_t H5HF__cache_dblock_get_initial_load_size(void *udata, size_t *image_len);
88
static htri_t H5HF__cache_dblock_verify_chksum(const void *image_ptr, size_t len, void *udata_ptr);
89
static void  *H5HF__cache_dblock_deserialize(const void *image, size_t len, void *udata, bool *dirty);
90
static herr_t H5HF__cache_dblock_image_len(const void *thing, size_t *image_len);
91
static herr_t H5HF__cache_dblock_pre_serialize(H5F_t *f, void *thing, haddr_t addr, size_t len,
92
                                               haddr_t *new_addr, size_t *new_len, unsigned *flags);
93
static herr_t H5HF__cache_dblock_serialize(const H5F_t *f, void *image, size_t len, void *thing);
94
static herr_t H5HF__cache_dblock_notify(H5AC_notify_action_t action, void *thing);
95
static herr_t H5HF__cache_dblock_free_icr(void *thing);
96
static herr_t H5HF__cache_dblock_fsf_size(const void *_thing, hsize_t *fsf_size);
97
98
/* Debugging Function Prototypes */
99
#ifndef NDEBUG
100
static herr_t H5HF__cache_verify_hdr_descendants_clean(H5F_t *f, H5HF_hdr_t *hdr, bool *fd_clean,
101
                                                       bool *clean);
102
static herr_t H5HF__cache_verify_iblock_descendants_clean(H5F_t *f, haddr_t fd_parent_addr,
103
                                                          H5HF_indirect_t *iblock, unsigned *iblock_status,
104
                                                          bool *fd_clean, bool *clean);
105
static herr_t H5HF__cache_verify_iblocks_dblocks_clean(H5F_t *f, haddr_t fd_parent_addr,
106
                                                       H5HF_indirect_t *iblock, bool *fd_clean, bool *clean,
107
                                                       bool *has_dblocks);
108
static herr_t H5HF__cache_verify_descendant_iblocks_clean(H5F_t *f, haddr_t fd_parent_addr,
109
                                                          H5HF_indirect_t *iblock, bool *fd_clean,
110
                                                          bool *clean, bool *has_iblocks);
111
#endif /* NDEBUG */
112
113
/*********************/
114
/* Package Variables */
115
/*********************/
116
117
/* H5HF header inherits cache-like properties from H5AC */
118
const H5AC_class_t H5AC_FHEAP_HDR[1] = {{
119
    H5AC_FHEAP_HDR_ID,                     /* Metadata client ID */
120
    "fractal heap header",                 /* Metadata client name (for debugging) */
121
    H5FD_MEM_FHEAP_HDR,                    /* File space memory type for client */
122
    H5AC__CLASS_SPECULATIVE_LOAD_FLAG,     /* Client class behavior flags */
123
    H5HF__cache_hdr_get_initial_load_size, /* 'get_initial_load_size' callback */
124
    H5HF__cache_hdr_get_final_load_size,   /* 'get_final_load_size' callback */
125
    H5HF__cache_hdr_verify_chksum,         /* 'verify_chksum' callback */
126
    H5HF__cache_hdr_deserialize,           /* 'deserialize' callback */
127
    H5HF__cache_hdr_image_len,             /* 'image_len' callback */
128
    H5HF__cache_hdr_pre_serialize,         /* 'pre_serialize' callback */
129
    H5HF__cache_hdr_serialize,             /* 'serialize' callback */
130
    NULL,                                  /* 'notify' callback */
131
    H5HF__cache_hdr_free_icr,              /* 'free_icr' callback */
132
    NULL,                                  /* 'fsf_size' callback */
133
}};
134
135
/* H5HF indirect block inherits cache-like properties from H5AC */
136
const H5AC_class_t H5AC_FHEAP_IBLOCK[1] = {{
137
    H5AC_FHEAP_IBLOCK_ID,                     /* Metadata client ID */
138
    "fractal heap indirect block",            /* Metadata client name (for debugging) */
139
    H5FD_MEM_FHEAP_IBLOCK,                    /* File space memory type for client */
140
    H5AC__CLASS_NO_FLAGS_SET,                 /* Client class behavior flags */
141
    H5HF__cache_iblock_get_initial_load_size, /* 'get_initial_load_size' callback */
142
    NULL,                                     /* 'get_final_load_size' callback */
143
    H5HF__cache_iblock_verify_chksum,         /* 'verify_chksum' callback */
144
    H5HF__cache_iblock_deserialize,           /* 'deserialize' callback */
145
    H5HF__cache_iblock_image_len,             /* 'image_len' callback */
146
    H5HF__cache_iblock_pre_serialize,         /* 'pre_serialize' callback */
147
    H5HF__cache_iblock_serialize,             /* 'serialize' callback */
148
    H5HF__cache_iblock_notify,                /* 'notify' callback */
149
    H5HF__cache_iblock_free_icr,              /* 'free_icr' callback */
150
    NULL,                                     /* 'fsf_size' callback */
151
}};
152
153
/* H5HF direct block inherits cache-like properties from H5AC */
154
const H5AC_class_t H5AC_FHEAP_DBLOCK[1] = {{
155
    H5AC_FHEAP_DBLOCK_ID,                     /* Metadata client ID */
156
    "fractal heap direct block",              /* Metadata client name (for debugging) */
157
    H5FD_MEM_FHEAP_DBLOCK,                    /* File space memory type for client */
158
    H5AC__CLASS_NO_FLAGS_SET,                 /* Client class behavior flags */
159
    H5HF__cache_dblock_get_initial_load_size, /* 'get_initial_load_size' callback */
160
    NULL,                                     /* 'get_final_load_size' callback */
161
    H5HF__cache_dblock_verify_chksum,         /* 'verify_chksum' callback */
162
    H5HF__cache_dblock_deserialize,           /* 'deserialize' callback */
163
    H5HF__cache_dblock_image_len,             /* 'image_len' callback */
164
    H5HF__cache_dblock_pre_serialize,         /* 'pre_serialize' callback */
165
    H5HF__cache_dblock_serialize,             /* 'serialize' callback */
166
    H5HF__cache_dblock_notify,                /* 'notify' callback */
167
    H5HF__cache_dblock_free_icr,              /* 'free_icr' callback */
168
    H5HF__cache_dblock_fsf_size,              /* 'fsf_size' callback */
169
}};
170
171
/*****************************/
172
/* Library Private Variables */
173
/*****************************/
174
175
/*******************/
176
/* Local Variables */
177
/*******************/
178
179
/* Declare a free list to manage heap direct block data to/from disk */
180
H5FL_BLK_DEFINE(direct_block);
181
182
/*-------------------------------------------------------------------------
183
 * Function:  H5HF__hdr_prefix_decode()
184
 *
185
 * Purpose: Decode a fractal heap header's prefix
186
 *
187
 * Return:  Success:  SUCCEED
188
 *    Failure:  FAIL
189
 *
190
 *-------------------------------------------------------------------------
191
 */
192
static herr_t
193
H5HF__hdr_prefix_decode(H5HF_hdr_t *hdr, const uint8_t **image_ref)
194
0
{
195
0
    const uint8_t *image     = *image_ref; /* Pointer into into supplied image */
196
0
    herr_t         ret_value = SUCCEED;    /* Return value */
197
198
0
    FUNC_ENTER_PACKAGE
199
200
    /* Sanity checks */
201
0
    assert(hdr);
202
0
    assert(image);
203
204
    /* Magic number */
205
0
    if (memcmp(image, H5HF_HDR_MAGIC, (size_t)H5_SIZEOF_MAGIC) != 0)
206
0
        HGOTO_ERROR(H5E_HEAP, H5E_BADVALUE, FAIL, "wrong fractal heap header signature");
207
0
    image += H5_SIZEOF_MAGIC;
208
209
    /* Version */
210
0
    if (*image++ != H5HF_HDR_VERSION)
211
0
        HGOTO_ERROR(H5E_HEAP, H5E_VERSION, FAIL, "wrong fractal heap header version");
212
213
    /* General heap information */
214
0
    UINT16DECODE(image, hdr->id_len);     /* Heap ID length */
215
0
    UINT16DECODE(image, hdr->filter_len); /* I/O filters' encoded length */
216
217
    /* Update the image buffer pointer */
218
0
    *image_ref = image;
219
220
0
done:
221
0
    FUNC_LEAVE_NOAPI(ret_value)
222
0
} /* end H5HF__hdr_prefix_decode() */
223
224
/*-------------------------------------------------------------------------
225
 * Function:  H5HF__dtable_decode
226
 *
227
 * Purpose: Decodes the metadata for a doubling table
228
 *
229
 * Return:  Success:  Pointer to a new fractal heap
230
 *
231
 *    Failure:  NULL
232
 *
233
 *-------------------------------------------------------------------------
234
 */
235
static herr_t
236
H5HF__dtable_decode(H5F_t *f, const uint8_t **pp, H5HF_dtable_t *dtable)
237
0
{
238
0
    FUNC_ENTER_PACKAGE_NOERR
239
240
    /* Check arguments */
241
0
    assert(f);
242
0
    assert(pp && *pp);
243
0
    assert(dtable);
244
245
    /* Table width */
246
0
    UINT16DECODE(*pp, dtable->cparam.width);
247
248
    /* Starting block size */
249
0
    H5F_DECODE_LENGTH(f, *pp, dtable->cparam.start_block_size);
250
251
    /* Maximum direct block size */
252
0
    H5F_DECODE_LENGTH(f, *pp, dtable->cparam.max_direct_size);
253
254
    /* Maximum heap size (as # of bits) */
255
0
    UINT16DECODE(*pp, dtable->cparam.max_index);
256
257
    /* Starting # of rows in root indirect block */
258
0
    UINT16DECODE(*pp, dtable->cparam.start_root_rows);
259
260
    /* Address of table */
261
0
    H5F_addr_decode(f, pp, &(dtable->table_addr));
262
263
    /* Current # of rows in root indirect block */
264
0
    UINT16DECODE(*pp, dtable->curr_root_rows);
265
266
0
    FUNC_LEAVE_NOAPI(SUCCEED)
267
0
} /* end H5HF__dtable_decode() */
268
269
/*-------------------------------------------------------------------------
270
 * Function:  H5HF__dtable_encode
271
 *
272
 * Purpose: Encodes the metadata for a doubling table
273
 *
274
 * Return:  Success:  Pointer to a new fractal heap
275
 *
276
 *    Failure:  NULL
277
 *
278
 *-------------------------------------------------------------------------
279
 */
280
static herr_t
281
H5HF__dtable_encode(H5F_t *f, uint8_t **pp, const H5HF_dtable_t *dtable)
282
0
{
283
0
    FUNC_ENTER_PACKAGE_NOERR
284
285
    /* Check arguments */
286
0
    assert(f);
287
0
    assert(pp && *pp);
288
0
    assert(dtable);
289
290
    /* Table width */
291
0
    UINT16ENCODE(*pp, dtable->cparam.width);
292
293
    /* Starting block size */
294
0
    H5F_ENCODE_LENGTH(f, *pp, dtable->cparam.start_block_size);
295
296
    /* Maximum direct block size */
297
0
    H5F_ENCODE_LENGTH(f, *pp, dtable->cparam.max_direct_size);
298
299
    /* Maximum heap size (as # of bits) */
300
0
    UINT16ENCODE(*pp, dtable->cparam.max_index);
301
302
    /* Starting # of rows in root indirect block */
303
0
    UINT16ENCODE(*pp, dtable->cparam.start_root_rows);
304
305
    /* Address of root direct/indirect block */
306
0
    H5F_addr_encode(f, pp, dtable->table_addr);
307
308
    /* Current # of rows in root indirect block */
309
0
    UINT16ENCODE(*pp, dtable->curr_root_rows);
310
311
0
    FUNC_LEAVE_NOAPI(SUCCEED)
312
0
} /* end H5HF__dtable_encode() */
313
314
/*-------------------------------------------------------------------------
315
 * Function:  H5HF__cache_hdr_get_initial_load_size()
316
 *
317
 * Purpose: Determine the size of the fractal heap header on disk,
318
 *    and set *image_len to this value.
319
 *
320
 *    Note also that the value returned by this function presumes that
321
 *    there is no I/O filtering data in the header.  If there is, the
322
 *    size reported will be too small, and H5C__load_entry()
323
 *    will have to make two tries to load the fractal heap header.
324
 *
325
 * Return:  Success:  SUCCEED
326
 *    Failure:  FAIL
327
 *
328
 *-------------------------------------------------------------------------
329
 */
330
static herr_t
331
H5HF__cache_hdr_get_initial_load_size(void *_udata, size_t *image_len)
332
0
{
333
0
    H5HF_hdr_cache_ud_t *udata = (H5HF_hdr_cache_ud_t *)_udata; /* Pointer to user data */
334
0
    H5HF_hdr_t           dummy_hdr;                             /* Dummy header -- to compute size */
335
336
0
    FUNC_ENTER_PACKAGE_NOERR
337
338
    /* Sanity checks */
339
0
    assert(udata);
340
0
    assert(image_len);
341
342
    /* Set the internal parameters for the heap */
343
0
    dummy_hdr.f           = udata->f;
344
0
    dummy_hdr.sizeof_size = H5F_SIZEOF_SIZE(udata->f);
345
0
    dummy_hdr.sizeof_addr = H5F_SIZEOF_ADDR(udata->f);
346
347
    /* Compute the 'base' size of the fractal heap header on disk */
348
0
    *image_len = (size_t)H5HF_HEADER_SIZE(&dummy_hdr);
349
350
0
    FUNC_LEAVE_NOAPI(SUCCEED)
351
0
} /* end H5HF__cache_hdr_get_initial_load_size() */
352
353
/*-------------------------------------------------------------------------
354
 * Function:  H5HF__cache_hdr_get_final_load_size()
355
 *
356
 * Purpose: Determine the final size of the fractal heap header on disk,
357
 *    and set *actual_len to this value.
358
 *
359
 * Return:  Success:  SUCCEED
360
 *    Failure:  FAIL
361
 *
362
 *-------------------------------------------------------------------------
363
 */
364
static herr_t
365
H5HF__cache_hdr_get_final_load_size(const void *_image, size_t H5_ATTR_NDEBUG_UNUSED image_len, void *_udata,
366
                                    size_t *actual_len)
367
0
{
368
0
    H5HF_hdr_t           hdr;                                       /* Temporary fractal heap header */
369
0
    const uint8_t       *image     = (const uint8_t *)_image;       /* Pointer into into supplied image */
370
0
    H5HF_hdr_cache_ud_t *udata     = (H5HF_hdr_cache_ud_t *)_udata; /* User data for callback */
371
0
    herr_t               ret_value = SUCCEED;                       /* Return value */
372
373
0
    FUNC_ENTER_PACKAGE
374
375
    /* Sanity checks */
376
0
    assert(image);
377
0
    assert(udata);
378
0
    assert(actual_len);
379
0
    assert(*actual_len == image_len);
380
381
    /* Deserialize the fractal heap header's prefix */
382
0
    if (H5HF__hdr_prefix_decode(&hdr, &image) < 0)
383
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTDECODE, FAIL, "can't decode fractal heap header prefix");
384
385
    /* Check for I/O filter info on this heap */
386
0
    if (hdr.filter_len > 0)
387
        /* Compute the extra heap header size */
388
        *actual_len += (size_t)(H5F_SIZEOF_SIZE(udata->f) /* Size of size for filtered root direct block */
389
0
                                + (unsigned)4      /* Size of filter mask for filtered root direct block */
390
0
                                + hdr.filter_len); /* Size of encoded I/O filter info */
391
392
0
done:
393
0
    FUNC_LEAVE_NOAPI(ret_value)
394
0
} /* end H5HF__cache_hdr_get_final_load_size() */
395
396
/*-------------------------------------------------------------------------
397
 * Function:    H5HF__cache_hdr_verify_chksum
398
 *
399
 * Purpose:     Verify the computed checksum of the data structure is the
400
 *              same as the stored chksum.
401
 *
402
 * Return:      Success:        true/false
403
 *              Failure:        Negative
404
 *
405
 *-------------------------------------------------------------------------
406
 */
407
static htri_t
408
H5HF__cache_hdr_verify_chksum(const void *_image, size_t len, void H5_ATTR_UNUSED *_udata)
409
0
{
410
0
    const uint8_t *image = (const uint8_t *)_image; /* Pointer into raw data buffer */
411
0
    uint32_t       stored_chksum;                   /* Stored metadata checksum value */
412
0
    uint32_t       computed_chksum;                 /* Computed metadata checksum value */
413
0
    htri_t         ret_value = true;                /* Return value */
414
415
0
    FUNC_ENTER_PACKAGE
416
417
    /* Check arguments */
418
0
    assert(image);
419
420
    /* Get stored and computed checksums */
421
0
    if (H5F_get_checksums(image, len, &stored_chksum, &computed_chksum) < 0)
422
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't get checksums");
423
424
0
    if (stored_chksum != computed_chksum)
425
0
        ret_value = false;
426
427
0
done:
428
0
    FUNC_LEAVE_NOAPI(ret_value)
429
0
} /* end H5HF__cache_hdr_verify_chksum() */
430
431
/*-------------------------------------------------------------------------
432
 * Function:  H5HF__cache_hdr_deserialize
433
 *
434
 * Purpose: Given a buffer containing an on disk image of a fractal heap
435
 *    header block, allocate an instance of H5HF_hdr_t, load the contents
436
 *    of the buffer into into the new instance of H5HF_hdr_t, and then
437
 *    return a pointer to the new instance.
438
 *
439
 * Return:  Success:  Pointer to in core representation
440
 *    Failure:  NULL
441
 *
442
 *-------------------------------------------------------------------------
443
 */
444
static void *
445
H5HF__cache_hdr_deserialize(const void *_image, size_t len, void *_udata, bool H5_ATTR_UNUSED *dirty)
446
0
{
447
0
    H5HF_hdr_t          *hdr   = NULL;                          /* Fractal heap info */
448
0
    H5HF_hdr_cache_ud_t *udata = (H5HF_hdr_cache_ud_t *)_udata; /* User data for callback */
449
0
    const uint8_t       *image = (const uint8_t *)_image;       /* Pointer into into supplied image */
450
0
    uint32_t             stored_chksum;                         /* Stored metadata checksum value */
451
0
    uint8_t              heap_flags;                            /* Status flags for heap */
452
0
    void                *ret_value = NULL;                      /* Return value */
453
454
0
    FUNC_ENTER_PACKAGE
455
456
    /* Sanity checks */
457
0
    assert(image);
458
0
    assert(len > 0);
459
0
    assert(udata);
460
0
    assert(dirty);
461
462
    /* Allocate space for the fractal heap data structure */
463
0
    if (NULL == (hdr = H5HF__hdr_alloc(udata->f)))
464
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed");
465
466
    /* Deserialize the fractal heap header's prefix */
467
0
    if (H5HF__hdr_prefix_decode(hdr, &image) < 0)
468
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTDECODE, NULL, "can't decode fractal heap header prefix");
469
470
    /* Heap status flags */
471
    /* (bit 0: "huge" object IDs have wrapped) */
472
    /* (bit 1: checksum direct blocks) */
473
0
    heap_flags            = *image++;
474
0
    hdr->huge_ids_wrapped = heap_flags & H5HF_HDR_FLAGS_HUGE_ID_WRAPPED;
475
0
    hdr->checksum_dblocks = heap_flags & H5HF_HDR_FLAGS_CHECKSUM_DBLOCKS;
476
477
    /* "Huge" object information */
478
0
    UINT32DECODE(image, hdr->max_man_size);                 /* Max. size of "managed" objects */
479
0
    H5F_DECODE_LENGTH(udata->f, image, hdr->huge_next_id);  /* Next ID to use for "huge" object */
480
0
    H5F_addr_decode(udata->f, &image, &hdr->huge_bt2_addr); /* Address of "huge" object tracker B-tree */
481
482
    /* "Managed" object free space information */
483
0
    H5F_DECODE_LENGTH(udata->f, image,
484
0
                      hdr->total_man_free);           /* Internal free space in managed direct blocks */
485
0
    H5F_addr_decode(udata->f, &image, &hdr->fs_addr); /* Address of free section header */
486
487
    /* Heap statistics */
488
0
    H5F_DECODE_LENGTH(udata->f, image, hdr->man_size);
489
0
    H5F_DECODE_LENGTH(udata->f, image, hdr->man_alloc_size);
490
0
    H5F_DECODE_LENGTH(udata->f, image, hdr->man_iter_off);
491
0
    H5F_DECODE_LENGTH(udata->f, image, hdr->man_nobjs);
492
0
    H5F_DECODE_LENGTH(udata->f, image, hdr->huge_size);
493
0
    H5F_DECODE_LENGTH(udata->f, image, hdr->huge_nobjs);
494
0
    H5F_DECODE_LENGTH(udata->f, image, hdr->tiny_size);
495
0
    H5F_DECODE_LENGTH(udata->f, image, hdr->tiny_nobjs);
496
497
    /* Managed objects' doubling-table info */
498
0
    if (H5HF__dtable_decode(hdr->f, &image, &(hdr->man_dtable)) < 0)
499
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTENCODE, NULL, "unable to encode managed obj. doubling table info");
500
501
    /* Set the fractal heap header's 'base' size */
502
0
    hdr->heap_size = (size_t)H5HF_HEADER_SIZE(hdr);
503
504
    /* Sanity check */
505
    /* (allow for checksum not decoded yet) */
506
0
    assert((size_t)(image - (const uint8_t *)_image) == (hdr->heap_size - H5HF_SIZEOF_CHKSUM));
507
508
    /* Check for I/O filter information to decode */
509
0
    if (hdr->filter_len > 0) {
510
0
        H5O_pline_t *pline; /* Pipeline information from the header on disk */
511
512
        /* Sanity check */
513
0
        assert(len > hdr->heap_size); /* A header with filter info is > than a standard header */
514
515
        /* Compute the heap header's size */
516
0
        hdr->heap_size += (size_t)(hdr->sizeof_size /* Size of size for filtered root direct block */
517
0
                                   + (unsigned)4    /* Size of filter mask for filtered root direct block */
518
0
                                   + hdr->filter_len); /* Size of encoded I/O filter info */
519
520
        /* Decode the size of a filtered root direct block */
521
0
        H5F_DECODE_LENGTH(udata->f, image, hdr->pline_root_direct_size);
522
523
        /* Decode the filter mask for a filtered root direct block */
524
0
        UINT32DECODE(image, hdr->pline_root_direct_filter_mask);
525
526
        /* Decode I/O filter information */
527
0
        if (NULL == (pline = (H5O_pline_t *)H5O_msg_decode(hdr->f, NULL, H5O_PLINE_ID, len, image)))
528
0
            HGOTO_ERROR(H5E_HEAP, H5E_CANTDECODE, NULL, "can't decode I/O pipeline filters");
529
530
        /* Advance past filter info to checksum */
531
0
        image += hdr->filter_len;
532
533
        /* Copy the information into the header's I/O pipeline structure */
534
0
        if (NULL == H5O_msg_copy(H5O_PLINE_ID, pline, &(hdr->pline)))
535
0
            HGOTO_ERROR(H5E_HEAP, H5E_CANTCOPY, NULL, "can't copy I/O filter pipeline");
536
537
        /* Release the space allocated for the I/O pipeline filters */
538
0
        H5O_msg_free(H5O_PLINE_ID, pline);
539
0
    } /* end if */
540
541
    /* Metadata checksum */
542
0
    UINT32DECODE(image, stored_chksum);
543
544
    /* Sanity check */
545
0
    assert((size_t)(image - (const uint8_t *)_image) == hdr->heap_size);
546
547
    /* Finish initialization of heap header */
548
0
    if (H5HF__hdr_finish_init(hdr) < 0)
549
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_CANTINIT, NULL, "can't finish initializing shared fractal heap header");
550
551
    /* Set return value */
552
0
    ret_value = (void *)hdr;
553
554
0
done:
555
0
    if (!ret_value && hdr)
556
0
        if (H5HF__hdr_free(hdr) < 0)
557
0
            HDONE_ERROR(H5E_HEAP, H5E_CANTRELEASE, NULL, "unable to release fractal heap header");
558
559
0
    FUNC_LEAVE_NOAPI(ret_value)
560
0
} /* end H5HF__cache_hdr_deserialize() */
561
562
/*-------------------------------------------------------------------------
563
 * Function:  H5HF__cache_hdr_image_len
564
 *
565
 * Purpose: Return the actual size of the fractal heap header on
566
 *    disk image.
567
 *
568
 *    If the header contains filter information, this size will be
569
 *    larger than the value returned by H5HF__cache_hdr_get_initial_load_size().
570
 *
571
 * Return:  Success:  SUCCEED
572
 *    Failure:  FAIL
573
 *
574
 *-------------------------------------------------------------------------
575
 */
576
static herr_t
577
H5HF__cache_hdr_image_len(const void *_thing, size_t *image_len)
578
0
{
579
0
    const H5HF_hdr_t *hdr = (const H5HF_hdr_t *)_thing; /* Fractal heap info */
580
581
0
    FUNC_ENTER_PACKAGE_NOERR
582
583
    /* Sanity checks */
584
0
    assert(hdr);
585
0
    assert(hdr->cache_info.type == H5AC_FHEAP_HDR);
586
0
    assert(image_len);
587
588
0
    *image_len = hdr->heap_size;
589
590
0
    FUNC_LEAVE_NOAPI(SUCCEED)
591
0
} /* end H5HF__cache_hdr_image_len() */
592
593
/*-------------------------------------------------------------------------
594
 * Function:  H5HF__cache_hdr_pre_serialize
595
 *
596
 * Purpose: As best I can tell, fractal heap header blocks are always
597
 *    allocated in real file space.  Thus this routine simply verifies
598
 *    this, verifies that the len parameter contains the expected
599
 *    value, and returns an error if either of these checks fail.
600
 *
601
 *    When compiled in debug mode, the function also verifies that all
602
 *    indirect and direct blocks that are children of the header are
603
 *    either clean, or not in the metadata cache.
604
 *
605
 * Return:  Success:  SUCCEED
606
 *    Failure:  FAIL
607
 *
608
 *-------------------------------------------------------------------------
609
 */
610
static herr_t
611
H5HF__cache_hdr_pre_serialize(H5F_t *f, void *_thing, haddr_t addr, size_t len,
612
                              haddr_t H5_ATTR_UNUSED *new_addr, size_t H5_ATTR_UNUSED *new_len,
613
                              unsigned *flags)
614
0
{
615
0
    H5HF_hdr_t *hdr       = (H5HF_hdr_t *)_thing; /* Fractal heap info */
616
0
    herr_t      ret_value = SUCCEED;              /* Return value */
617
618
0
    FUNC_ENTER_PACKAGE
619
620
    /* Sanity checks */
621
0
    assert(f);
622
0
    assert(hdr);
623
0
    assert(hdr->cache_info.type == H5AC_FHEAP_HDR);
624
0
    assert(H5_addr_defined(addr));
625
0
    assert(addr == hdr->heap_addr);
626
0
    assert(new_addr);
627
0
    assert(new_len);
628
0
    assert(flags);
629
630
#ifndef NDEBUG
631
    {
632
        bool descendants_clean = true;
633
        bool fd_children_clean = true;
634
635
        /* Verify that flush dependencies are working correctly.  Do this
636
         * by verifying that either:
637
         *
638
         * 1) the header has a root iblock, and that the root iblock and all
639
         *    of its children are clean, or
640
         *
641
         * 2) The header has a root dblock, which is clean, or
642
         *
643
         * 3) The heap is empty, and thus the header has neither a root
644
         *    iblock no a root dblock.  In this case, the flush ordering
645
         *    constraint is met by default.
646
         *
647
         * Do this with a call to H5HF__cache_verify_hdr_descendants_clean().
648
         *
649
         * Note that descendants need not be clean if the pre_serialize call
650
         * is made during a cache serialization instead of an entry or cache
651
         * flush.
652
         *
653
         * Note also that with the recent change in the definition of flush
654
         * dependency, not all descendants need be clean -- only direct flush
655
         * dependency children.
656
         *
657
         * Finally, observe that the H5HF__cache_verify_hdr_descendants_clean()
658
         * call still looks for dirty descendants.  At present we do not check
659
         * this value.
660
         */
661
        if (H5HF__cache_verify_hdr_descendants_clean((H5F_t *)f, hdr, &fd_children_clean,
662
                                                     &descendants_clean) < 0)
663
            HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, "can't verify hdr descendants clean.");
664
        assert(fd_children_clean);
665
    }
666
#endif /* NDEBUG */
667
668
0
    if (H5F_IS_TMP_ADDR(f, addr))
669
0
        HGOTO_ERROR(H5E_HEAP, H5E_BADVALUE, FAIL, "addr in temporary space?!?.");
670
671
0
    if (len != hdr->heap_size)
672
0
        HGOTO_ERROR(H5E_HEAP, H5E_BADVALUE, FAIL, "unexpected image len.");
673
674
0
    *flags = 0;
675
676
0
done:
677
0
    FUNC_LEAVE_NOAPI(ret_value)
678
0
} /* end H5HF__cache_hdr_pre_serialize() */
679
680
/*-------------------------------------------------------------------------
681
 * Function:  H5HF__cache_hdr_serialize
682
 *
683
 * Purpose: Construct the on disk image of the header, and place it in
684
 *    the buffer pointed to by image.  Return SUCCEED on success,
685
 *    and FAIL on failure.
686
 *
687
 * Return:  Success:  SUCCEED
688
 *    Failure:  FAIL
689
 *
690
 *-------------------------------------------------------------------------
691
 */
692
static herr_t
693
H5HF__cache_hdr_serialize(const H5F_t *f, void *_image, size_t H5_ATTR_NDEBUG_UNUSED len, void *_thing)
694
0
{
695
0
    H5HF_hdr_t *hdr   = (H5HF_hdr_t *)_thing; /* Fractal heap info */
696
0
    uint8_t    *image = (uint8_t *)_image;    /* Pointer into raw data buffer */
697
0
    uint8_t     heap_flags;                   /* Status flags for heap */
698
0
    uint32_t    metadata_chksum;              /* Computed metadata checksum value */
699
0
    herr_t      ret_value = SUCCEED;          /* Return value */
700
701
0
    FUNC_ENTER_PACKAGE
702
703
    /* Sanity checks */
704
0
    assert(f);
705
0
    assert(image);
706
0
    assert(hdr);
707
0
    assert(hdr->cache_info.type == H5AC_FHEAP_HDR);
708
0
    assert(len == hdr->heap_size);
709
710
    /* Set the shared heap header's file context for this operation */
711
0
    H5_GCC_DIAG_OFF("discarded-qualifiers")
712
0
    H5_CLANG_DIAG_OFF("incompatible-pointer-types-discards-qualifiers")
713
0
    hdr->f = f;
714
0
    H5_GCC_DIAG_ON("discarded-qualifiers")
715
0
    H5_CLANG_DIAG_ON("incompatible-pointer-types-discards-qualifiers")
716
717
    /* Magic number */
718
0
    H5MM_memcpy(image, H5HF_HDR_MAGIC, (size_t)H5_SIZEOF_MAGIC);
719
0
    image += H5_SIZEOF_MAGIC;
720
721
    /* Version # */
722
0
    *image++ = H5HF_HDR_VERSION;
723
724
    /* General heap information */
725
0
    UINT16ENCODE(image, hdr->id_len);     /* Heap ID length */
726
0
    UINT16ENCODE(image, hdr->filter_len); /* I/O filters' encoded length */
727
728
    /* Heap status flags */
729
    /* (bit 0: "huge" object IDs have wrapped) */
730
    /* (bit 1: checksum direct blocks) */
731
0
    heap_flags = 0;
732
0
    heap_flags = (uint8_t)(heap_flags | (hdr->huge_ids_wrapped ? H5HF_HDR_FLAGS_HUGE_ID_WRAPPED : 0));
733
0
    heap_flags = (uint8_t)(heap_flags | (hdr->checksum_dblocks ? H5HF_HDR_FLAGS_CHECKSUM_DBLOCKS : 0));
734
0
    *image++   = heap_flags;
735
736
    /* "Huge" object information */
737
0
    UINT32ENCODE(image, hdr->max_man_size);         /* Max. size of "managed" objects */
738
0
    H5F_ENCODE_LENGTH(f, image, hdr->huge_next_id); /* Next ID to use for "huge" object */
739
0
    H5F_addr_encode(f, &image, hdr->huge_bt2_addr); /* Address of "huge" object tracker B-tree */
740
741
    /* "Managed" object free space information */
742
0
    H5F_ENCODE_LENGTH(f, image, hdr->total_man_free); /* Internal free space in managed direct blocks */
743
0
    H5F_addr_encode(f, &image, hdr->fs_addr);         /* Address of free section header */
744
745
    /* Heap statistics */
746
0
    H5F_ENCODE_LENGTH(f, image, hdr->man_size);
747
0
    H5F_ENCODE_LENGTH(f, image, hdr->man_alloc_size);
748
0
    H5F_ENCODE_LENGTH(f, image, hdr->man_iter_off);
749
0
    H5F_ENCODE_LENGTH(f, image, hdr->man_nobjs);
750
0
    H5F_ENCODE_LENGTH(f, image, hdr->huge_size);
751
0
    H5F_ENCODE_LENGTH(f, image, hdr->huge_nobjs);
752
0
    H5F_ENCODE_LENGTH(f, image, hdr->tiny_size);
753
0
    H5F_ENCODE_LENGTH(f, image, hdr->tiny_nobjs);
754
755
    /* Managed objects' doubling-table info */
756
0
    if (H5HF__dtable_encode(hdr->f, &image, &(hdr->man_dtable)) < 0)
757
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTENCODE, FAIL, "unable to encode managed obj. doubling table info");
758
759
    /* Check for I/O filter information to encode */
760
0
    if (hdr->filter_len > 0) {
761
        /* Encode the size of a filtered root direct block */
762
0
        H5F_ENCODE_LENGTH(f, image, hdr->pline_root_direct_size);
763
764
        /* Encode the filter mask for a filtered root direct block */
765
0
        UINT32ENCODE(image, hdr->pline_root_direct_filter_mask);
766
767
        /* Encode I/O filter information */
768
0
        if (H5O_msg_encode(hdr->f, H5O_PLINE_ID, false, image, &(hdr->pline)) < 0)
769
0
            HGOTO_ERROR(H5E_HEAP, H5E_CANTENCODE, FAIL, "can't encode I/O pipeline filters");
770
0
        image += hdr->filter_len;
771
0
    } /* end if */
772
773
    /* Compute metadata checksum */
774
0
    metadata_chksum = H5_checksum_metadata(_image, (size_t)(image - (uint8_t *)_image), 0);
775
776
    /* Metadata checksum */
777
0
    UINT32ENCODE(image, metadata_chksum);
778
779
    /* sanity check */
780
0
    assert((size_t)(image - (uint8_t *)_image) == len);
781
782
0
done:
783
0
    FUNC_LEAVE_NOAPI(ret_value)
784
0
} /* end H5HF__cache_hdr_serialize() */
785
786
/*-------------------------------------------------------------------------
787
 * Function:  H5HF__cache_hdr_free_icr
788
 *
789
 * Purpose: Free the in core representation of the fractal heap header.
790
 *
791
 *    This routine frees just the header itself, not the
792
 *    associated version 2 B-Tree, the associated Free Space Manager,
793
 *    nor the indirect/direct block tree that is rooted in the header.
794
 *
795
 *    This routine also does not free the file space that may
796
 *    be allocated to the header.
797
 *
798
 * Return:  Success:  SUCCEED
799
 *    Failure:  FAIL
800
 *
801
 *-------------------------------------------------------------------------
802
 */
803
static herr_t
804
H5HF__cache_hdr_free_icr(void *_thing)
805
0
{
806
0
    H5HF_hdr_t *hdr       = (H5HF_hdr_t *)_thing; /* Fractal heap info */
807
0
    herr_t      ret_value = SUCCEED;              /* Return value */
808
809
0
    FUNC_ENTER_PACKAGE
810
811
    /* Sanity checks */
812
0
    assert(hdr);
813
0
    assert(hdr->cache_info.type == H5AC_FHEAP_HDR);
814
0
    assert(hdr->rc == 0);
815
816
0
    if (H5HF__hdr_free(hdr) < 0)
817
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTRELEASE, FAIL, "unable to release fractal heap header");
818
819
0
done:
820
0
    FUNC_LEAVE_NOAPI(ret_value)
821
0
} /* end H5HF__cache_hdr_free_icr() */
822
823
/*-------------------------------------------------------------------------
824
 * Function:  H5HF__cache_iblock_get_initial_load_size()
825
 *
826
 * Purpose: Compute the size of the on disk image of the indirect
827
 *    block, and place this value in *image_len.
828
 *
829
 * Return:  Success:  SUCCEED
830
 *    Failure:  FAIL
831
 *
832
 *-------------------------------------------------------------------------
833
 */
834
static herr_t
835
H5HF__cache_iblock_get_initial_load_size(void *_udata, size_t *image_len)
836
0
{
837
0
    H5HF_iblock_cache_ud_t *udata = (H5HF_iblock_cache_ud_t *)_udata; /* User data for callback */
838
839
0
    FUNC_ENTER_PACKAGE_NOERR
840
841
    /* Sanity checks */
842
0
    assert(udata);
843
0
    assert(udata->par_info);
844
0
    assert(udata->par_info->hdr);
845
0
    assert(image_len);
846
847
    /* Set the image length size */
848
0
    *image_len = (size_t)H5HF_MAN_INDIRECT_SIZE(udata->par_info->hdr, *udata->nrows);
849
850
0
    FUNC_LEAVE_NOAPI(SUCCEED)
851
0
} /* end H5HF__cache_iblock_get_initial_load_size() */
852
853
/*-------------------------------------------------------------------------
854
 * Function:    H5HF__cache_iblock_verify_chksum
855
 *
856
 * Purpose:     Verify the computed checksum of the data structure is the
857
 *              same as the stored checksum.
858
 *
859
 * Return:      Success:        true/false
860
 *              Failure:        Negative
861
 *
862
 *-------------------------------------------------------------------------
863
 */
864
static htri_t
865
H5HF__cache_iblock_verify_chksum(const void *_image, size_t len, void H5_ATTR_UNUSED *_udata)
866
0
{
867
0
    const uint8_t *image = (const uint8_t *)_image; /* Pointer into raw data buffer */
868
0
    uint32_t       stored_chksum;                   /* Stored metadata checksum value */
869
0
    uint32_t       computed_chksum;                 /* Computed metadata checksum value */
870
0
    htri_t         ret_value = true;                /* Return value */
871
872
0
    FUNC_ENTER_PACKAGE
873
874
    /* Check arguments */
875
0
    assert(image);
876
877
    /* Get stored and computed checksums */
878
0
    if (H5F_get_checksums(image, len, &stored_chksum, &computed_chksum) < 0)
879
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't get checksums");
880
881
0
    if (stored_chksum != computed_chksum)
882
0
        ret_value = false;
883
884
0
done:
885
0
    FUNC_LEAVE_NOAPI(ret_value)
886
0
} /* end H5HF__cache_iblock_verify_chksum() */
887
888
/*-------------------------------------------------------------------------
889
 * Function:  H5HF__cache_iblock_deserialize
890
 *
891
 * Purpose: Given a buffer containing the on disk image of the indirect
892
 *    block, allocate an instance of H5HF_indirect_t, load the data
893
 *    in the buffer into this new instance, and return a pointer to
894
 *    it.
895
 *
896
 *    As best I can tell, the size of the indirect block image is fully
897
 *    know before the image is loaded, so this function should succeed
898
 *    unless the image is corrupt or memory allocation fails.
899
 *
900
 * Return:  Success:  Pointer to in core representation
901
 *    Failure:  NULL
902
 *
903
 *-------------------------------------------------------------------------
904
 */
905
static void *
906
H5HF__cache_iblock_deserialize(const void *_image, size_t H5_ATTR_NDEBUG_UNUSED len, void *_udata,
907
                               bool H5_ATTR_UNUSED *dirty)
908
0
{
909
0
    H5HF_hdr_t             *hdr;                                       /* Shared fractal heap information */
910
0
    H5HF_iblock_cache_ud_t *udata  = (H5HF_iblock_cache_ud_t *)_udata; /* User data for callback */
911
0
    H5HF_indirect_t        *iblock = NULL;                             /* Indirect block info */
912
0
    const uint8_t          *image  = (const uint8_t *)_image;          /* Pointer into raw data buffer */
913
0
    haddr_t                 heap_addr;        /* Address of heap header in the file */
914
0
    uint32_t                stored_chksum;    /* Stored metadata checksum value */
915
0
    unsigned                u;                /* Local index variable */
916
0
    void                   *ret_value = NULL; /* Return value */
917
918
0
    FUNC_ENTER_PACKAGE
919
920
    /* Sanity checks */
921
0
    assert(image);
922
0
    assert(udata);
923
0
    assert(dirty);
924
0
    hdr = udata->par_info->hdr;
925
0
    assert(hdr->f);
926
927
    /* Set the shared heap header's file context for this operation */
928
0
    hdr->f = udata->f;
929
930
    /* Allocate space for the fractal heap indirect block */
931
0
    if (NULL == (iblock = H5FL_CALLOC(H5HF_indirect_t)))
932
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed");
933
934
    /* Share common heap information */
935
0
    iblock->hdr = hdr;
936
0
    if (H5HF__hdr_incr(hdr) < 0)
937
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTINC, NULL, "can't increment reference count on shared heap header");
938
939
    /* Set block's internal information */
940
0
    iblock->rc        = 0;
941
0
    iblock->nrows     = *udata->nrows;
942
0
    iblock->nchildren = 0;
943
944
    /* Compute size of indirect block */
945
0
    iblock->size = H5HF_MAN_INDIRECT_SIZE(hdr, iblock->nrows);
946
947
    /* sanity check */
948
0
    assert(iblock->size == len);
949
950
    /* Magic number */
951
0
    if (memcmp(image, H5HF_IBLOCK_MAGIC, (size_t)H5_SIZEOF_MAGIC) != 0)
952
0
        HGOTO_ERROR(H5E_HEAP, H5E_BADVALUE, NULL, "wrong fractal heap indirect block signature");
953
0
    image += H5_SIZEOF_MAGIC;
954
955
    /* Version */
956
0
    if (*image++ != H5HF_IBLOCK_VERSION)
957
0
        HGOTO_ERROR(H5E_HEAP, H5E_VERSION, NULL, "wrong fractal heap direct block version");
958
959
    /* Address of heap that owns this block */
960
0
    H5F_addr_decode(udata->f, &image, &heap_addr);
961
0
    if (H5_addr_ne(heap_addr, hdr->heap_addr))
962
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTLOAD, NULL, "incorrect heap header address for direct block");
963
964
    /* Address of parent block */
965
0
    iblock->parent = udata->par_info->iblock;
966
    /* this copy of the parent pointer is needed by the notify callback so */
967
    /* that it can take down flush dependencies on eviction even if        */
968
    /* the parent pointer has been nulled out.             JRM -- 5/18/14  */
969
0
    if (udata->par_info->iblock)
970
0
        iblock->fd_parent = udata->par_info->iblock;
971
0
    else
972
0
        iblock->fd_parent = udata->par_info->hdr;
973
0
    iblock->par_entry = udata->par_info->entry;
974
0
    if (iblock->parent) {
975
        /* Share parent block */
976
0
        if (H5HF__iblock_incr(iblock->parent) < 0)
977
0
            HGOTO_ERROR(H5E_HEAP, H5E_CANTINC, NULL,
978
0
                        "can't increment reference count on shared indirect block");
979
980
        /* Set max. # of rows in this block */
981
0
        iblock->max_rows = iblock->nrows;
982
0
    } /* end if */
983
0
    else {
984
        /* Set max. # of rows in this block */
985
0
        iblock->max_rows = hdr->man_dtable.max_root_rows;
986
0
    } /* end else */
987
988
    /* Offset of heap within the heap's address space */
989
0
    UINT64DECODE_VAR(image, iblock->block_off, hdr->heap_off_size);
990
991
    /* Allocate & decode child block entry tables */
992
0
    assert(iblock->nrows > 0);
993
0
    if (NULL == (iblock->ents = H5FL_SEQ_MALLOC(H5HF_indirect_ent_t,
994
0
                                                (size_t)(iblock->nrows * hdr->man_dtable.cparam.width))))
995
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed for direct entries");
996
997
0
    if (hdr->filter_len > 0) {
998
0
        unsigned dir_rows; /* Number of direct rows in this indirect block */
999
1000
        /* Compute the number of direct rows for this indirect block */
1001
0
        dir_rows = MIN(iblock->nrows, hdr->man_dtable.max_direct_rows);
1002
1003
        /* Allocate indirect block filtered entry array */
1004
0
        if (NULL == (iblock->filt_ents = H5FL_SEQ_MALLOC(H5HF_indirect_filt_ent_t,
1005
0
                                                         (size_t)(dir_rows * hdr->man_dtable.cparam.width))))
1006
0
            HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed for block entries");
1007
0
    } /* end if */
1008
0
    else
1009
0
        iblock->filt_ents = NULL;
1010
1011
0
    for (u = 0; u < (iblock->nrows * hdr->man_dtable.cparam.width); u++) {
1012
        /* Decode child block address */
1013
0
        H5F_addr_decode(udata->f, &image, &(iblock->ents[u].addr));
1014
1015
        /* Check for heap with I/O filters */
1016
0
        if (hdr->filter_len > 0) {
1017
            /* Sanity check */
1018
0
            assert(iblock->filt_ents);
1019
1020
            /* Decode extra information for direct blocks */
1021
0
            if (u < (hdr->man_dtable.max_direct_rows * hdr->man_dtable.cparam.width)) {
1022
                /* Size of filtered direct block */
1023
0
                H5F_DECODE_LENGTH(udata->f, image, iblock->filt_ents[u].size);
1024
1025
                /* Sanity check */
1026
                /* (either both the address & size are defined or both are
1027
                 *  not defined)
1028
                 */
1029
0
                assert((H5_addr_defined(iblock->ents[u].addr) && iblock->filt_ents[u].size) ||
1030
0
                       (!H5_addr_defined(iblock->ents[u].addr) && iblock->filt_ents[u].size == 0));
1031
1032
                /* I/O filter mask for filtered direct block */
1033
0
                UINT32DECODE(image, iblock->filt_ents[u].filter_mask);
1034
0
            } /* end if */
1035
0
        }     /* end if */
1036
1037
        /* Count child blocks */
1038
0
        if (H5_addr_defined(iblock->ents[u].addr)) {
1039
0
            iblock->nchildren++;
1040
0
            iblock->max_child = u;
1041
0
        } /* end if */
1042
0
    }     /* end for */
1043
1044
    /* Sanity check */
1045
0
    assert(iblock->nchildren); /* indirect blocks w/no children should have been deleted */
1046
1047
    /* checksum verification already done by verify_chksum cb */
1048
1049
    /* Metadata checksum */
1050
0
    UINT32DECODE(image, stored_chksum);
1051
1052
    /* Sanity check */
1053
0
    assert((size_t)(image - (const uint8_t *)_image) == iblock->size);
1054
1055
    /* Check if we have any indirect block children */
1056
0
    if (iblock->nrows > hdr->man_dtable.max_direct_rows) {
1057
0
        unsigned indir_rows; /* Number of indirect rows in this indirect block */
1058
1059
        /* Compute the number of indirect rows for this indirect block */
1060
0
        indir_rows = iblock->nrows - hdr->man_dtable.max_direct_rows;
1061
1062
        /* Allocate & initialize child indirect block pointer array */
1063
0
        if (NULL == (iblock->child_iblocks = H5FL_SEQ_CALLOC(
1064
0
                         H5HF_indirect_ptr_t, (size_t)(indir_rows * hdr->man_dtable.cparam.width))))
1065
0
            HGOTO_ERROR(H5E_HEAP, H5E_NOSPACE, NULL, "memory allocation failed for block entries");
1066
0
    } /* end if */
1067
0
    else
1068
0
        iblock->child_iblocks = NULL;
1069
1070
    /* Set return value */
1071
0
    ret_value = (void *)iblock;
1072
1073
0
done:
1074
0
    if (!ret_value && iblock)
1075
0
        if (H5HF__man_iblock_dest(iblock) < 0)
1076
0
            HDONE_ERROR(H5E_HEAP, H5E_CANTFREE, NULL, "unable to destroy fractal heap indirect block");
1077
1078
0
    FUNC_LEAVE_NOAPI(ret_value)
1079
0
} /* end H5HF__cache_iblock_deserialize() */
1080
1081
/*-------------------------------------------------------------------------
1082
 * Function:  H5HF__cache_iblock_image_len
1083
 *
1084
 * Purpose: Return the size of the on disk image of the iblock.
1085
 *
1086
 * Return:  Success:  SUCCEED
1087
 *    Failure:  FAIL
1088
 *
1089
 *-------------------------------------------------------------------------
1090
 */
1091
static herr_t
1092
H5HF__cache_iblock_image_len(const void *_thing, size_t *image_len)
1093
0
{
1094
0
    const H5HF_indirect_t *iblock = (const H5HF_indirect_t *)_thing; /* Indirect block info */
1095
1096
0
    FUNC_ENTER_PACKAGE_NOERR
1097
1098
    /* Sanity checks */
1099
0
    assert(iblock);
1100
0
    assert(iblock->cache_info.type == H5AC_FHEAP_IBLOCK);
1101
0
    assert(image_len);
1102
1103
0
    *image_len = iblock->size;
1104
1105
0
    FUNC_LEAVE_NOAPI(SUCCEED)
1106
0
} /* end H5HF__cache_iblock_image_len() */
1107
1108
/*-------------------------------------------------------------------------
1109
 * Function:  H5HF__cache_iblock_pre_serialize
1110
 *
1111
 * Purpose: The primary objective of this function is to determine if the
1112
 *    indirect block is currently allocated in temporary file space,
1113
 *    and if so, to move it to real file space before the entry is
1114
 *    serialized.
1115
 *
1116
 *    In debug compiles, this function also verifies that all
1117
 *    immediate flush dependency children of this indirect block
1118
 *    are either clean or are not in cache.
1119
 *
1120
 * Return:  Success:  SUCCEED
1121
 *    Failure:  FAIL
1122
 *
1123
 *-------------------------------------------------------------------------
1124
 */
1125
static herr_t
1126
H5HF__cache_iblock_pre_serialize(H5F_t *f, void *_thing, haddr_t addr, size_t H5_ATTR_UNUSED len,
1127
                                 haddr_t *new_addr, size_t H5_ATTR_UNUSED *new_len, unsigned *flags)
1128
0
{
1129
0
    H5HF_hdr_t      *hdr;                                   /* Shared fractal heap information */
1130
0
    H5HF_indirect_t *iblock    = (H5HF_indirect_t *)_thing; /* Indirect block info */
1131
0
    herr_t           ret_value = SUCCEED;                   /* Return value */
1132
1133
0
    FUNC_ENTER_PACKAGE
1134
1135
    /* Sanity checks */
1136
0
    assert(f);
1137
0
    assert(iblock);
1138
0
    assert(iblock->cache_info.type == H5AC_FHEAP_IBLOCK);
1139
0
    assert(iblock->cache_info.size == iblock->size);
1140
0
    assert(H5_addr_defined(addr));
1141
0
    assert(H5_addr_eq(iblock->addr, addr));
1142
0
    assert(new_addr);
1143
0
    assert(new_len);
1144
0
    assert(flags);
1145
0
    hdr = iblock->hdr;
1146
0
    assert(hdr);
1147
0
    assert(hdr->cache_info.type == H5AC_FHEAP_HDR);
1148
1149
#ifndef NDEBUG
1150
    {
1151
        bool     descendants_clean = true;
1152
        bool     fd_children_clean = true;
1153
        unsigned iblock_status     = 0;
1154
1155
        /* verify that flush dependencies are working correctly.  Do this
1156
         * by verifying that all immediate flush dependency children of this
1157
         * iblock are clean.
1158
         */
1159
        if (H5AC_get_entry_status(f, iblock->addr, &iblock_status) < 0)
1160
            HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't get iblock status");
1161
1162
        /* since the current iblock is the guest of honor in a flush, we know
1163
         * that it is locked into the cache for the duration of the call.  Hence
1164
         * there is no need to check to see if it is pinned or protected, or to
1165
         * protect it if it is not.
1166
         */
1167
        if (H5HF__cache_verify_iblock_descendants_clean((H5F_t *)f, iblock->addr, iblock, &iblock_status,
1168
                                                        &fd_children_clean, &descendants_clean) < 0)
1169
            HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, "can't verify descendants clean.");
1170
        assert(fd_children_clean);
1171
    }
1172
#endif /* NDEBUG */
1173
1174
    /* Check to see if we must re-allocate the iblock from temporary to
1175
     * normal (AKA real) file space.
1176
     */
1177
0
    if (H5F_IS_TMP_ADDR(f, addr)) {
1178
0
        haddr_t iblock_addr;
1179
1180
        /* Allocate 'normal' space for the new indirect block on disk */
1181
0
        if (HADDR_UNDEF ==
1182
0
            (iblock_addr = H5MF_alloc((H5F_t *)f, H5FD_MEM_FHEAP_IBLOCK, (hsize_t)iblock->size)))
1183
0
            HGOTO_ERROR(H5E_HEAP, H5E_NOSPACE, FAIL,
1184
0
                        "file allocation failed for fractal heap indirect block");
1185
1186
        /* Sanity check */
1187
0
        assert(!H5_addr_eq(iblock->addr, iblock_addr));
1188
1189
        /* Let the metadata cache know the block moved */
1190
0
        if (H5AC_move_entry((H5F_t *)f, H5AC_FHEAP_IBLOCK, iblock->addr, iblock_addr) < 0)
1191
0
            HGOTO_ERROR(H5E_HEAP, H5E_CANTMOVE, FAIL, "unable to move indirect block");
1192
1193
        /* Update the internal address for the block */
1194
0
        iblock->addr = iblock_addr;
1195
1196
        /* Check for root indirect block */
1197
0
        if (NULL == iblock->parent) {
1198
            /* Update information about indirect block's location */
1199
0
            hdr->man_dtable.table_addr = iblock_addr;
1200
1201
            /* Mark that heap header was modified */
1202
0
            if (H5HF__hdr_dirty(hdr) < 0)
1203
0
                HGOTO_ERROR(H5E_HEAP, H5E_CANTDIRTY, FAIL, "can't mark heap header as dirty");
1204
0
        } /* end if */
1205
0
        else {
1206
0
            H5HF_indirect_t *par_iblock; /* Parent indirect block */
1207
0
            unsigned         par_entry;  /* Entry in parent indirect block */
1208
1209
            /* Get parent information */
1210
0
            par_iblock = iblock->parent;
1211
0
            par_entry  = iblock->par_entry;
1212
1213
            /* Update information about indirect block's location */
1214
0
            par_iblock->ents[par_entry].addr = iblock_addr;
1215
1216
            /* Mark that parent was modified */
1217
0
            if (H5HF__iblock_dirty(par_iblock) < 0)
1218
0
                HGOTO_ERROR(H5E_HEAP, H5E_CANTDIRTY, FAIL, "can't mark heap header as dirty");
1219
0
        } /* end if */
1220
1221
0
        *new_addr = iblock_addr;
1222
0
        *flags    = H5AC__SERIALIZE_MOVED_FLAG;
1223
0
    } /* end if */
1224
0
    else
1225
0
        *flags = 0;
1226
1227
0
done:
1228
0
    FUNC_LEAVE_NOAPI(ret_value)
1229
0
} /* end H5HF__cache_iblock_pre_serialize() */
1230
1231
/*-------------------------------------------------------------------------
1232
 * Function:  H5HF__cache_iblock_serialize
1233
 *
1234
 * Purpose: Given a pointer to an iblock, and a pointer to a buffer of
1235
 *    the appropriate size, write the contents of the iblock to the
1236
 *    buffer in format appropriate for writing to disk.
1237
 *
1238
 * Return:  Success:  SUCCEED
1239
 *    Failure:  FAIL
1240
 *
1241
 *-------------------------------------------------------------------------
1242
 */
1243
static herr_t
1244
H5HF__cache_iblock_serialize(const H5F_t *f, void *_image, size_t H5_ATTR_NDEBUG_UNUSED len, void *_thing)
1245
0
{
1246
0
    H5HF_hdr_t      *hdr;                                /* Shared fractal heap information */
1247
0
    H5HF_indirect_t *iblock = (H5HF_indirect_t *)_thing; /* Indirect block info */
1248
0
    uint8_t         *image  = (uint8_t *)_image;         /* Pointer into raw data buffer */
1249
#ifndef NDEBUG
1250
    unsigned nchildren = 0;       /* Track # of children */
1251
    size_t   max_child = 0;       /* Track max. child entry used */
1252
#endif                            /* NDEBUG */
1253
0
    uint32_t metadata_chksum;     /* Computed metadata checksum value */
1254
0
    size_t   u;                   /* Local index variable */
1255
0
    herr_t   ret_value = SUCCEED; /* Return value */
1256
1257
0
    FUNC_ENTER_PACKAGE_NOERR
1258
1259
    /* Sanity checks */
1260
0
    assert(f);
1261
0
    assert(image);
1262
0
    assert(iblock);
1263
0
    assert(iblock->cache_info.type == H5AC_FHEAP_IBLOCK);
1264
0
    assert(iblock->cache_info.size == iblock->size);
1265
0
    assert(len == iblock->size);
1266
1267
    /* Indirect block must be in 'normal' file space */
1268
0
    assert(!H5F_IS_TMP_ADDR(f, iblock->addr));
1269
0
    assert(H5_addr_eq(iblock->addr, iblock->cache_info.addr));
1270
1271
    /* Get the pointer to the shared heap header */
1272
0
    hdr = iblock->hdr;
1273
1274
    /* Set the shared heap header's file context for this operation */
1275
0
    H5_GCC_DIAG_OFF("discarded-qualifiers")
1276
0
    H5_CLANG_DIAG_OFF("incompatible-pointer-types-discards-qualifiers")
1277
0
    hdr->f = f;
1278
0
    H5_GCC_DIAG_ON("discarded-qualifiers")
1279
0
    H5_CLANG_DIAG_ON("incompatible-pointer-types-discards-qualifiers")
1280
1281
    /* Magic number */
1282
0
    H5MM_memcpy(image, H5HF_IBLOCK_MAGIC, (size_t)H5_SIZEOF_MAGIC);
1283
0
    image += H5_SIZEOF_MAGIC;
1284
1285
    /* Version # */
1286
0
    *image++ = H5HF_IBLOCK_VERSION;
1287
1288
    /* Address of heap header for heap which owns this block */
1289
0
    H5F_addr_encode(f, &image, hdr->heap_addr);
1290
1291
    /* Offset of block in heap */
1292
0
    UINT64ENCODE_VAR(image, iblock->block_off, hdr->heap_off_size);
1293
1294
    /* Encode indirect block-specific fields */
1295
0
    for (u = 0; u < (iblock->nrows * hdr->man_dtable.cparam.width); u++) {
1296
        /* Encode child block address */
1297
0
        H5F_addr_encode(f, &image, iblock->ents[u].addr);
1298
1299
        /* Check for heap with I/O filters */
1300
0
        if (hdr->filter_len > 0) {
1301
            /* Sanity check */
1302
0
            assert(iblock->filt_ents);
1303
1304
            /* Encode extra information for direct blocks */
1305
0
            if (u < (hdr->man_dtable.max_direct_rows * hdr->man_dtable.cparam.width)) {
1306
                /* Sanity check */
1307
                /* (either both the address & size are defined or both are
1308
                 *  not defined)
1309
                 */
1310
0
                assert((H5_addr_defined(iblock->ents[u].addr) && iblock->filt_ents[u].size) ||
1311
0
                       (!H5_addr_defined(iblock->ents[u].addr) && iblock->filt_ents[u].size == 0));
1312
1313
                /* Size of filtered direct block */
1314
0
                H5F_ENCODE_LENGTH(f, image, iblock->filt_ents[u].size);
1315
1316
                /* I/O filter mask for filtered direct block */
1317
0
                UINT32ENCODE(image, iblock->filt_ents[u].filter_mask);
1318
0
            } /* end if */
1319
0
        }     /* end if */
1320
1321
#ifndef NDEBUG
1322
        /* Count child blocks */
1323
        if (H5_addr_defined(iblock->ents[u].addr)) {
1324
            nchildren++;
1325
            if (u > max_child)
1326
                max_child = u;
1327
        } /* end if */
1328
#endif    /* NDEBUG */
1329
0
    }     /* end for */
1330
1331
    /* Compute checksum */
1332
0
    metadata_chksum = H5_checksum_metadata((uint8_t *)_image, (size_t)(image - (uint8_t *)_image), 0);
1333
1334
    /* Metadata checksum */
1335
0
    UINT32ENCODE(image, metadata_chksum);
1336
1337
    /* Sanity checks */
1338
0
    assert((size_t)(image - (uint8_t *)_image) == iblock->size);
1339
#ifndef NDEBUG
1340
    assert(nchildren == iblock->nchildren);
1341
    assert(max_child == iblock->max_child);
1342
#endif /* NDEBUG */
1343
1344
0
    FUNC_LEAVE_NOAPI(ret_value)
1345
0
} /* end H5HF__cache_iblock_serialize() */
1346
1347
/*-------------------------------------------------------------------------
1348
 * Function:  H5HF__cache_iblock_notify
1349
 *
1350
 * Purpose: This function is used to create and destroy flush dependency
1351
 *    relationships between iblocks and their parents as indirect blocks
1352
 *    are loaded / inserted and evicted from the metadata cache.
1353
 *
1354
 *    In general, the parent will be another iblock, but it may be the
1355
 *    header if the iblock in question is the root iblock.
1356
 *
1357
 * Return:  Success:  SUCCEED
1358
 *    Failure:  FAIL
1359
 *
1360
 *-------------------------------------------------------------------------
1361
 */
1362
static herr_t
1363
H5HF__cache_iblock_notify(H5AC_notify_action_t action, void *_thing)
1364
0
{
1365
0
    H5HF_indirect_t *iblock    = (H5HF_indirect_t *)_thing; /* Indirect block info */
1366
0
    herr_t           ret_value = SUCCEED;                   /* Return value */
1367
1368
0
    FUNC_ENTER_PACKAGE
1369
1370
    /* Sanity checks */
1371
0
    assert(iblock);
1372
0
    assert(iblock->cache_info.type == H5AC_FHEAP_IBLOCK);
1373
0
    assert(iblock->hdr);
1374
1375
    /* further sanity checks */
1376
0
    if (iblock->parent == NULL) {
1377
        /* pointer from hdr to root iblock will not be set up unless */
1378
        /* the fractal heap has already pinned the hdr.  Do what     */
1379
        /* sanity checking we can.                                   */
1380
0
        if ((iblock->block_off == 0) && (iblock->hdr->root_iblock_flags & H5HF_ROOT_IBLOCK_PINNED))
1381
0
            assert(iblock->hdr->root_iblock == iblock);
1382
0
    } /* end if */
1383
0
    else {
1384
        /* if this is a child iblock, verify that the pointers are */
1385
        /* either uninitialized or set up correctly.               */
1386
0
        H5HF_indirect_t H5_ATTR_NDEBUG_UNUSED *par_iblock = iblock->parent;
1387
0
        unsigned H5_ATTR_NDEBUG_UNUSED         indir_idx; /* Index in parent's child iblock pointer array */
1388
1389
        /* Sanity check */
1390
0
        assert(par_iblock->child_iblocks);
1391
0
        assert(iblock->par_entry >=
1392
0
               (iblock->hdr->man_dtable.max_direct_rows * iblock->hdr->man_dtable.cparam.width));
1393
1394
        /* Compute index in parent's child iblock pointer array */
1395
0
        indir_idx = iblock->par_entry -
1396
0
                    (iblock->hdr->man_dtable.max_direct_rows * iblock->hdr->man_dtable.cparam.width);
1397
1398
        /* The pointer to iblock in the parent may not be set yet -- */
1399
        /* verify that it is either NULL, or that it has been set to */
1400
        /* iblock.                                                   */
1401
0
        assert((NULL == par_iblock->child_iblocks[indir_idx]) ||
1402
0
               (par_iblock->child_iblocks[indir_idx] == iblock));
1403
0
    } /* end else */
1404
1405
0
    switch (action) {
1406
0
        case H5AC_NOTIFY_ACTION_AFTER_INSERT:
1407
0
        case H5AC_NOTIFY_ACTION_AFTER_LOAD:
1408
            /* Create flush dependency with parent, if there is one */
1409
0
            if (iblock->fd_parent)
1410
0
                if (H5AC_create_flush_dependency(iblock->fd_parent, iblock) < 0)
1411
0
                    HGOTO_ERROR(H5E_HEAP, H5E_CANTDEPEND, FAIL, "unable to create flush dependency");
1412
0
            break;
1413
1414
0
        case H5AC_NOTIFY_ACTION_AFTER_FLUSH:
1415
0
        case H5AC_NOTIFY_ACTION_ENTRY_DIRTIED:
1416
0
        case H5AC_NOTIFY_ACTION_ENTRY_CLEANED:
1417
0
        case H5AC_NOTIFY_ACTION_CHILD_DIRTIED:
1418
0
        case H5AC_NOTIFY_ACTION_CHILD_CLEANED:
1419
0
        case H5AC_NOTIFY_ACTION_CHILD_UNSERIALIZED:
1420
0
        case H5AC_NOTIFY_ACTION_CHILD_SERIALIZED:
1421
            /* do nothing */
1422
0
            break;
1423
1424
0
        case H5AC_NOTIFY_ACTION_BEFORE_EVICT:
1425
0
            if (iblock->fd_parent) {
1426
                /* Destroy flush dependency with parent */
1427
0
                if (H5AC_destroy_flush_dependency(iblock->fd_parent, iblock) < 0)
1428
0
                    HGOTO_ERROR(H5E_HEAP, H5E_CANTUNDEPEND, FAIL, "unable to destroy flush dependency");
1429
0
                iblock->fd_parent = NULL;
1430
0
            } /* end if */
1431
0
            break;
1432
1433
0
        default:
1434
0
            HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "unknown action from metadata cache");
1435
0
            break;
1436
0
    } /* end switch */
1437
1438
0
done:
1439
0
    FUNC_LEAVE_NOAPI(ret_value)
1440
0
} /* end H5HF__cache_iblock_notify() */
1441
1442
/*-------------------------------------------------------------------------
1443
 * Function:  H5HF__cache_iblock_free_icr
1444
 *
1445
 * Purpose: Unlink the supplied instance of H5HF_indirect_t from the
1446
 *    fractal heap and free its memory.
1447
 *
1448
 * Return:  Success:  SUCCEED
1449
 *    Failure:  FAIL
1450
 *
1451
 *-------------------------------------------------------------------------
1452
 */
1453
static herr_t
1454
H5HF__cache_iblock_free_icr(void *thing)
1455
0
{
1456
0
    H5HF_indirect_t *iblock    = (H5HF_indirect_t *)thing; /* Fractal heap indirect block to free */
1457
0
    herr_t           ret_value = SUCCEED;                  /* Return value */
1458
1459
0
    FUNC_ENTER_PACKAGE
1460
1461
    /* Sanity checks */
1462
0
    assert(iblock);
1463
0
    assert(iblock->cache_info.type == H5AC_FHEAP_IBLOCK);
1464
0
    assert(iblock->rc == 0);
1465
0
    assert(iblock->hdr);
1466
1467
    /* Destroy fractal heap indirect block */
1468
0
    if (H5HF__man_iblock_dest(iblock) < 0)
1469
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTFREE, FAIL, "unable to destroy fractal heap indirect block");
1470
1471
0
done:
1472
0
    FUNC_LEAVE_NOAPI(ret_value)
1473
0
} /* end H5HF__cache_iblock_free_icr() */
1474
1475
/*-------------------------------------------------------------------------
1476
 * Function:  H5HF__cache_dblock_get_initial_load_size()
1477
 *
1478
 * Purpose: Determine the size of the direct block on disk image, and
1479
 *    return it in *image_len.
1480
 *
1481
 * Return:  Success:  SUCCEED
1482
 *    Failure:  FAIL
1483
 *
1484
 *-------------------------------------------------------------------------
1485
 */
1486
static herr_t
1487
H5HF__cache_dblock_get_initial_load_size(void *_udata, size_t *image_len)
1488
0
{
1489
0
    const H5HF_dblock_cache_ud_t *udata = (const H5HF_dblock_cache_ud_t *)_udata; /* User data for callback */
1490
0
    const H5HF_parent_t          *par_info; /* Pointer to parent information */
1491
0
    const H5HF_hdr_t             *hdr;      /* Shared fractal heap information */
1492
1493
0
    FUNC_ENTER_PACKAGE_NOERR
1494
1495
    /* Sanity checks */
1496
0
    assert(udata);
1497
0
    assert(image_len);
1498
1499
    /* Convenience variables */
1500
0
    par_info = (const H5HF_parent_t *)(&(udata->par_info));
1501
0
    assert(par_info);
1502
0
    hdr = par_info->hdr;
1503
0
    assert(hdr);
1504
1505
    /* Check for I/O filters on this heap */
1506
0
    if (hdr->filter_len > 0) {
1507
        /* Check for root direct block */
1508
0
        if (par_info->iblock == NULL)
1509
            /* filtered root direct block */
1510
0
            *image_len = hdr->pline_root_direct_size;
1511
0
        else
1512
            /* filtered direct block */
1513
0
            *image_len = par_info->iblock->filt_ents[par_info->entry].size;
1514
0
    } /* end if */
1515
0
    else
1516
0
        *image_len = udata->dblock_size;
1517
1518
0
    FUNC_LEAVE_NOAPI(SUCCEED)
1519
0
} /* end H5HF__cache_dblock_get_initial_load_size() */
1520
1521
/*-------------------------------------------------------------------------
1522
 * Function:    H5HF__cache_dblock_verify_chksum
1523
 *
1524
 * Purpose:     Verify the computed checksum of the data structure is the
1525
 *              same as the stored chksum.
1526
 *
1527
 * Return:      Success:        true/false
1528
 *              Failure:        Negative
1529
 *
1530
 *-------------------------------------------------------------------------
1531
 */
1532
static htri_t
1533
H5HF__cache_dblock_verify_chksum(const void *_image, size_t len, void *_udata)
1534
0
{
1535
0
    const uint8_t          *image    = (const uint8_t *)_image;          /* Pointer into raw data buffer */
1536
0
    H5HF_dblock_cache_ud_t *udata    = (H5HF_dblock_cache_ud_t *)_udata; /* User data for callback */
1537
0
    void                   *read_buf = NULL;                             /* Pointer to buffer to read in */
1538
0
    H5HF_hdr_t             *hdr;                                         /* Shared fractal heap information */
1539
0
    H5HF_parent_t          *par_info;                                    /* Pointer to parent information */
1540
0
    uint32_t                stored_chksum;                               /* Stored metadata checksum value */
1541
0
    uint32_t                computed_chksum;  /* Computed metadata checksum value */
1542
0
    size_t                  chk_size;         /* The size for validating checksum */
1543
0
    uint8_t                *chk_p;            /* Pointer to the area for validating checksum */
1544
0
    htri_t                  ret_value = true; /* Return value */
1545
1546
0
    FUNC_ENTER_PACKAGE
1547
1548
    /* Sanity checks */
1549
0
    assert(image);
1550
0
    assert(udata);
1551
0
    par_info = (H5HF_parent_t *)(&(udata->par_info));
1552
0
    assert(par_info);
1553
0
    hdr = par_info->hdr;
1554
0
    assert(hdr);
1555
1556
    /* Get out if data block is not checksummed */
1557
0
    if (!(hdr->checksum_dblocks))
1558
0
        HGOTO_DONE(true);
1559
1560
0
    if (hdr->filter_len > 0) {
1561
0
        size_t   nbytes;      /* Number of bytes used in buffer, after applying reverse filters */
1562
0
        unsigned filter_mask; /* Excluded filters for direct block */
1563
0
        H5Z_cb_t filter_cb;   /* Filter callback structure */
1564
1565
        /* Initialize the filter callback struct */
1566
0
        filter_cb.op_data = NULL;
1567
0
        filter_cb.func    = NULL; /* no callback function when failed */
1568
1569
        /* Allocate buffer to perform I/O filtering on and copy image into
1570
         * it.  Must do this as H5Z_pipeline() may re-size the buffer
1571
         * provided to it.
1572
         */
1573
0
        if (NULL == (read_buf = H5MM_malloc(len)))
1574
0
            HGOTO_ERROR(H5E_HEAP, H5E_NOSPACE, FAIL, "memory allocation failed for pipeline buffer");
1575
1576
        /* Set up parameters for filter pipeline */
1577
0
        nbytes      = len;
1578
0
        filter_mask = udata->filter_mask;
1579
0
        H5MM_memcpy(read_buf, image, len);
1580
1581
        /* Push direct block data through I/O filter pipeline */
1582
0
        if (H5Z_pipeline(&(hdr->pline), H5Z_FLAG_REVERSE, &filter_mask, H5Z_ENABLE_EDC, filter_cb, &nbytes,
1583
0
                         &len, &read_buf) < 0)
1584
0
            HGOTO_ERROR(H5E_HEAP, H5E_CANTFILTER, FAIL, "output pipeline failed");
1585
1586
        /* Update info about direct block */
1587
0
        udata->decompressed = true;
1588
0
        len                 = nbytes;
1589
0
    }
1590
0
    else {
1591
        /* If the data are unfiltered, we just point to the image, which we
1592
         * never modify. Casting away const is okay here.
1593
         */
1594
0
        H5_GCC_CLANG_DIAG_OFF("cast-qual")
1595
0
        read_buf = (void *)image;
1596
0
        H5_GCC_CLANG_DIAG_OFF("cast-qual")
1597
0
    }
1598
1599
    /* Decode checksum */
1600
0
    chk_size = (size_t)(H5HF_MAN_ABS_DIRECT_OVERHEAD(hdr) - H5HF_SIZEOF_CHKSUM);
1601
0
    chk_p    = (uint8_t *)read_buf + chk_size;
1602
1603
    /* Metadata checksum */
1604
0
    UINT32DECODE(chk_p, stored_chksum);
1605
1606
0
    chk_p -= H5HF_SIZEOF_CHKSUM;
1607
1608
    /* Reset checksum field, for computing the checksum */
1609
0
    memset(chk_p, 0, (size_t)H5HF_SIZEOF_CHKSUM);
1610
1611
    /* Compute checksum on entire direct block */
1612
0
    computed_chksum = H5_checksum_metadata(read_buf, len, 0);
1613
1614
    /* Restore the checksum */
1615
0
    UINT32ENCODE(chk_p, stored_chksum);
1616
1617
    /* Verify checksum */
1618
0
    if (stored_chksum != computed_chksum)
1619
0
        HGOTO_DONE(false);
1620
1621
    /* Save the decompressed data to be used later in deserialize callback */
1622
0
    if (hdr->filter_len > 0) {
1623
        /* Sanity check */
1624
0
        assert(udata->decompressed);
1625
0
        assert(len == udata->dblock_size);
1626
1627
        /* Allocate block buffer */
1628
0
        if (NULL == (udata->dblk = H5FL_BLK_MALLOC(direct_block, (size_t)len)))
1629
0
            HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed");
1630
1631
        /* Copy un-filtered data into block's buffer */
1632
0
        H5MM_memcpy(udata->dblk, read_buf, len);
1633
0
    } /* end if */
1634
1635
0
done:
1636
    /* Release the read buffer */
1637
0
    if (read_buf && read_buf != image)
1638
0
        H5MM_xfree(read_buf);
1639
1640
0
    FUNC_LEAVE_NOAPI(ret_value)
1641
0
} /* end H5HF__cache_dblock_verify_chksum() */
1642
1643
/*-------------------------------------------------------------------------
1644
 * Function:  H5HF__cache_dblock_deserialize
1645
 *
1646
 * Purpose: Given a buffer containing the on disk image of a direct
1647
 *    block, allocate an instance of H5HF_direct_t, load the data
1648
 *    in the buffer into this new instance, and return a pointer to
1649
 *    it.
1650
 *
1651
 *    As best I can tell, the size of the direct block image is fully
1652
 *    know before the image is loaded, so this function should succeed
1653
 *    unless the image is corrupt or memory allocation fails.
1654
 *
1655
 * Return:  Success:  Pointer to in core representation
1656
 *    Failure:  NULL
1657
 *
1658
 *-------------------------------------------------------------------------
1659
 */
1660
static void *
1661
H5HF__cache_dblock_deserialize(const void *_image, size_t len, void *_udata, bool H5_ATTR_UNUSED *dirty)
1662
0
{
1663
0
    H5HF_hdr_t             *hdr;                                      /* Shared fractal heap information */
1664
0
    H5HF_dblock_cache_ud_t *udata = (H5HF_dblock_cache_ud_t *)_udata; /* User data for callback */
1665
0
    H5HF_parent_t          *par_info;                                 /* Pointer to parent information */
1666
0
    H5HF_direct_t          *dblock   = NULL;                          /* Direct block info */
1667
0
    const uint8_t          *image    = (const uint8_t *)_image;       /* Pointer into raw data buffer */
1668
0
    void                   *read_buf = NULL;                          /* Pointer to buffer to decompress */
1669
0
    haddr_t                 heap_addr;                                /* Address of heap header in the file */
1670
0
    void                   *ret_value = NULL;                         /* Return value */
1671
1672
0
    FUNC_ENTER_PACKAGE
1673
1674
    /* Sanity checks */
1675
0
    assert(image);
1676
0
    assert(udata);
1677
0
    par_info = (H5HF_parent_t *)(&(udata->par_info));
1678
0
    assert(par_info);
1679
0
    hdr = par_info->hdr;
1680
0
    assert(hdr);
1681
0
    assert(hdr->cache_info.type == H5AC_FHEAP_HDR);
1682
0
    assert(dirty);
1683
1684
    /* Allocate space for the fractal heap direct block */
1685
0
    if (NULL == (dblock = H5FL_CALLOC(H5HF_direct_t)))
1686
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed");
1687
0
    memset(&dblock->cache_info, 0, sizeof(H5AC_info_t));
1688
1689
    /* Set the shared heap header's file context for this operation */
1690
0
    hdr->f = udata->f;
1691
1692
    /* Share common heap information */
1693
0
    dblock->hdr = hdr;
1694
0
    if (H5HF__hdr_incr(hdr) < 0)
1695
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTINC, NULL, "can't increment reference count on shared heap header");
1696
1697
    /* Set block's internal information */
1698
0
    dblock->size = udata->dblock_size;
1699
1700
    /* Check for I/O filters on this heap */
1701
0
    if (hdr->filter_len > 0) {
1702
        /* Direct block is already decompressed in verify_chksum callback */
1703
0
        if (udata->decompressed) {
1704
            /* Sanity check */
1705
0
            assert(udata->dblk);
1706
1707
            /* Take ownership of the decompressed direct block */
1708
0
            dblock->blk = udata->dblk;
1709
0
            udata->dblk = NULL;
1710
0
        } /* end if */
1711
0
        else {
1712
0
            H5Z_cb_t filter_cb;   /* Filter callback structure */
1713
0
            size_t   nbytes;      /* Number of bytes used in buffer, after applying reverse filters */
1714
0
            unsigned filter_mask; /* Excluded filters for direct block */
1715
1716
            /* Sanity check */
1717
0
            assert(udata->dblk == NULL);
1718
1719
            /* Initialize the filter callback struct */
1720
0
            filter_cb.op_data = NULL;
1721
0
            filter_cb.func    = NULL; /* no callback function when failed */
1722
1723
            /* Allocate buffer to perform I/O filtering on and copy image into
1724
             * it.  Must do this as H5Z_pipeline() may resize the buffer
1725
             * provided to it.
1726
             */
1727
0
            if (NULL == (read_buf = H5MM_malloc(len)))
1728
0
                HGOTO_ERROR(H5E_HEAP, H5E_NOSPACE, NULL, "memory allocation failed for pipeline buffer");
1729
1730
            /* Copy compressed image into buffer */
1731
0
            H5MM_memcpy(read_buf, image, len);
1732
1733
            /* Push direct block data through I/O filter pipeline */
1734
0
            nbytes      = len;
1735
0
            filter_mask = udata->filter_mask;
1736
0
            if (H5Z_pipeline(&(hdr->pline), H5Z_FLAG_REVERSE, &filter_mask, H5Z_ENABLE_EDC, filter_cb,
1737
0
                             &nbytes, &len, &read_buf) < 0)
1738
0
                HGOTO_ERROR(H5E_HEAP, H5E_CANTFILTER, NULL, "output pipeline failed");
1739
1740
            /* Sanity check */
1741
0
            assert(nbytes == dblock->size);
1742
1743
            /* Copy un-filtered data into block's buffer */
1744
0
            H5MM_memcpy(dblock->blk, read_buf, dblock->size);
1745
0
        } /* end if */
1746
0
    }     /* end if */
1747
0
    else {
1748
        /* Sanity checks */
1749
0
        assert(udata->dblk == NULL);
1750
0
        assert(!udata->decompressed);
1751
1752
        /* Allocate block buffer */
1753
        /* XXX: Change to using free-list factories */
1754
0
        if (NULL == (dblock->blk = H5FL_BLK_MALLOC(direct_block, (size_t)dblock->size)))
1755
0
            HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed");
1756
1757
        /* Copy image to dblock->blk */
1758
0
        assert(dblock->size == len);
1759
0
        H5MM_memcpy(dblock->blk, image, dblock->size);
1760
0
    } /* end else */
1761
1762
    /* Start decoding direct block */
1763
0
    image = dblock->blk;
1764
1765
    /* Magic number */
1766
0
    if (memcmp(image, H5HF_DBLOCK_MAGIC, (size_t)H5_SIZEOF_MAGIC) != 0)
1767
0
        HGOTO_ERROR(H5E_HEAP, H5E_BADVALUE, NULL, "wrong fractal heap direct block signature");
1768
0
    image += H5_SIZEOF_MAGIC;
1769
1770
    /* Version */
1771
0
    if (*image++ != H5HF_DBLOCK_VERSION)
1772
0
        HGOTO_ERROR(H5E_HEAP, H5E_VERSION, NULL, "wrong fractal heap direct block version");
1773
1774
    /* Address of heap that owns this block (just for file integrity checks) */
1775
0
    H5F_addr_decode(udata->f, &image, &heap_addr);
1776
0
    if (H5_addr_ne(heap_addr, hdr->heap_addr))
1777
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTLOAD, NULL, "incorrect heap header address for direct block");
1778
1779
    /* Address of parent block */
1780
0
    dblock->parent = par_info->iblock;
1781
0
    if (par_info->iblock)
1782
0
        dblock->fd_parent = par_info->iblock;
1783
0
    else
1784
0
        dblock->fd_parent = par_info->hdr;
1785
0
    dblock->par_entry = par_info->entry;
1786
0
    if (dblock->parent) {
1787
        /* Share parent block */
1788
0
        if (H5HF__iblock_incr(dblock->parent) < 0)
1789
0
            HGOTO_ERROR(H5E_HEAP, H5E_CANTINC, NULL,
1790
0
                        "can't increment reference count on shared indirect block");
1791
0
    } /* end if */
1792
1793
    /* Offset of heap within the heap's address space */
1794
0
    UINT64DECODE_VAR(image, dblock->block_off, hdr->heap_off_size);
1795
1796
    /* Decode checksum on direct block, if requested */
1797
0
    if (hdr->checksum_dblocks) {
1798
0
        uint32_t stored_chksum; /* Metadata checksum value */
1799
1800
        /* checksum verification already done in verify_chksum cb */
1801
1802
        /* Metadata checksum */
1803
0
        UINT32DECODE(image, stored_chksum);
1804
0
    } /* end if */
1805
1806
    /* Sanity check */
1807
0
    assert((size_t)(image - dblock->blk) == (size_t)H5HF_MAN_ABS_DIRECT_OVERHEAD(hdr));
1808
1809
    /* Set return value */
1810
0
    ret_value = (void *)dblock;
1811
1812
0
done:
1813
    /* Release the read buffer */
1814
0
    if (read_buf)
1815
0
        H5MM_xfree(read_buf);
1816
1817
    /* Cleanup on error */
1818
0
    if (!ret_value && dblock)
1819
0
        if (H5HF__man_dblock_dest(dblock) < 0)
1820
0
            HDONE_ERROR(H5E_HEAP, H5E_CANTFREE, NULL, "unable to destroy fractal heap direct block");
1821
1822
0
    FUNC_LEAVE_NOAPI(ret_value)
1823
0
} /* end H5HF__cache_dblock_deserialize() */
1824
1825
/*-------------------------------------------------------------------------
1826
 * Function:  H5HF__cache_dblock_image_len
1827
 *
1828
 * Purpose: Report the actual size of the direct block image on disk.
1829
 *    Note that this value will probably be incorrect if compression
1830
 *    is enabled and the entry is dirty.
1831
 *
1832
 * Return:  Success:  SUCCEED
1833
 *    Failure:  FAIL
1834
 *
1835
 *-------------------------------------------------------------------------
1836
 */
1837
static herr_t
1838
H5HF__cache_dblock_image_len(const void *_thing, size_t *image_len)
1839
0
{
1840
0
    const H5HF_direct_t *dblock = (const H5HF_direct_t *)_thing; /* Direct block info */
1841
0
    const H5HF_hdr_t    *hdr;                                    /* Shared fractal heap information */
1842
0
    size_t               size;
1843
1844
0
    FUNC_ENTER_PACKAGE_NOERR
1845
1846
    /* Sanity checks */
1847
0
    assert(dblock);
1848
0
    assert(dblock->cache_info.type == H5AC_FHEAP_DBLOCK);
1849
0
    assert(image_len);
1850
1851
    /* Set up convenience variables */
1852
0
    hdr = dblock->hdr;
1853
0
    assert(hdr);
1854
1855
    /* Check for I/O filters on this heap */
1856
0
    if (hdr->filter_len > 0) {
1857
        /*
1858
         * If the data is available, set to the compressed
1859
         * size of the direct block -- otherwise set it equal to the
1860
         * uncompressed size.
1861
         *
1862
         * We have three possible scenarios here.
1863
         *
1864
         * First, the block may never have been flushed.  In this
1865
         * case, both dblock->file_size and the size stored in the
1866
         * parent (either the header or the parent iblock) will all
1867
         * be zero.  In this case, return the uncompressed size
1868
         * stored in dblock->size as the size.
1869
         *
1870
         * Second, the block may have just been serialized, in which
1871
         * case, dblock->file_size should be zero, and the correct
1872
         * on disk size should be stored in the parent (again, either
1873
         * the header or the parent iblock as case may be).
1874
         *
1875
         * Third, we may be in the process of discarding this
1876
         * dblock without writing it.  In this case, dblock->file_size
1877
         * should be non-zero and have the correct size.  Note that
1878
         * in this case, the direct block will have been detached,
1879
         * and thus looking up the parent will likely return incorrect
1880
         * data.
1881
         */
1882
0
        if (dblock->file_size != 0)
1883
0
            size = dblock->file_size;
1884
0
        else {
1885
0
            const H5HF_indirect_t *par_iblock = dblock->parent; /* Parent iblock */
1886
1887
0
            if (par_iblock)
1888
0
                size = par_iblock->filt_ents[dblock->par_entry].size;
1889
0
            else
1890
0
                size = hdr->pline_root_direct_size;
1891
1892
0
            if (size == 0)
1893
0
                size = dblock->size;
1894
0
        } /* end else */
1895
0
    }     /* end if */
1896
0
    else
1897
0
        size = dblock->size;
1898
1899
    /* Set the image size */
1900
0
    assert(size > 0);
1901
0
    *image_len = size;
1902
1903
0
    FUNC_LEAVE_NOAPI(SUCCEED)
1904
0
} /* end H5HF__cache_dblock_image_len() */
1905
1906
/*-------------------------------------------------------------------------
1907
 * Function:  H5HF__cache_dblock_pre_serialize
1908
 *
1909
 * Purpose: In principle, the purpose of this function is to determine
1910
 *    the size and location of the disk image of the target direct
1911
 *    block.  In this case, the uncompressed size of the block is
1912
 *    fixed, but since the direct block could be compressed,
1913
 *    we may need to compute and report the compressed size.
1914
 *
1915
 *    This is a bit sticky in the case of a direct block when I/O
1916
 *    filters are enabled, as the size of the compressed version
1917
 *    of the on disk image is not known until the direct block has
1918
 *    been run through the filters.  Further, the location of the
1919
 *    on disk image may change if the compressed size of the image
1920
 *    changes as well.
1921
 *
1922
 *    To complicate matters further, the direct block may have been
1923
 *    initially allocated in temporary (AKA imaginary) file space.
1924
 *    In this case, we must relocate the direct block's on-disk
1925
 *    image to "real" file space regardless of whether it has changed
1926
 *    size.
1927
 *
1928
 *    One simplifying factor is the direct block's "blk" field,
1929
 *    which contains a pointer to a buffer which (with the exception
1930
 *    of a small header) contains the on disk image in uncompressed
1931
 *    form.
1932
 *
1933
 *    To square this particular circle, this function does
1934
 *    everything the serialize function usually does, with the
1935
 *    exception of copying the image into the image buffer provided
1936
 *    to the serialize function by the metadata cache.  The data to
1937
 *    copy is provided to the serialize function in a buffer pointed
1938
 *    to by the write_buf field.
1939
 *
1940
 *    If I/O filters are enabled, on exit,
1941
 *    H5HF__cache_dblock_pre_serialize() sets the write_buf field to
1942
 *    point to a buffer containing the filtered image of the direct
1943
 *    block.  The serialize function should free this block, and set
1944
 *    the write_buf field to NULL after copying it into the image
1945
 *    buffer provided by the metadata cache.
1946
 *
1947
 *    If I/O filters are not enabled, this function prepares
1948
 *    the buffer pointed to by the blk field for copying to the
1949
 *    image buffer provided by the metadata cache, and sets the
1950
 *    write_buf field equal to the blk field.  In this case, the
1951
 *    serialize function should simply set the write_buf field to
1952
 *    NULL after copying the direct block image into the image
1953
 *    buffer.
1954
 *
1955
 *    In both of the above cases, the length of the buffer pointed
1956
 *    to by write_buf is provided in the write_len field.  This
1957
 *    field must contain 0 on entry to this function, and should
1958
 *    be set back to 0 at the end of the serialize function.
1959
 *
1960
 * Return:  Success:  SUCCEED
1961
 *    Failure:  FAIL
1962
 *
1963
 *-------------------------------------------------------------------------
1964
 */
1965
static herr_t
1966
H5HF__cache_dblock_pre_serialize(H5F_t *f, void *_thing, haddr_t addr, size_t len, haddr_t *new_addr,
1967
                                 size_t *new_len, unsigned *flags)
1968
0
{
1969
0
    bool at_tmp_addr; /* Flag to indicate direct block is */
1970
                      /* at temporary address */
1971
0
    haddr_t          dblock_addr;
1972
0
    H5HF_hdr_t      *hdr;                              /* Shared fractal heap information */
1973
0
    H5HF_direct_t   *dblock = (H5HF_direct_t *)_thing; /* Direct block info */
1974
0
    H5HF_indirect_t *par_iblock;                       /* Parent indirect block */
1975
0
    unsigned         par_entry = 0;                    /* Entry in parent indirect block */
1976
0
    void            *write_buf;                        /* Pointer to buffer to write out */
1977
0
    size_t           write_size;                       /* Size of buffer to write out */
1978
0
    uint8_t         *image;                            /* Pointer into raw data buffer */
1979
0
    unsigned         dblock_flags = 0;
1980
0
    herr_t           ret_value    = SUCCEED; /* Return value */
1981
1982
0
    FUNC_ENTER_PACKAGE
1983
1984
    /* Sanity checks */
1985
0
    assert(f);
1986
0
    assert(dblock);
1987
0
    assert(dblock->cache_info.type == H5AC_FHEAP_DBLOCK);
1988
0
    assert(dblock->write_buf == NULL);
1989
0
    assert(dblock->write_size == 0);
1990
0
    assert(dblock->cache_info.size == len);
1991
0
    assert(H5_addr_defined(addr));
1992
0
    assert(new_addr);
1993
0
    assert(new_len);
1994
0
    assert(flags);
1995
1996
    /* Set up local variables */
1997
0
    hdr         = dblock->hdr;
1998
0
    dblock_addr = addr; /* will update dblock_addr if we move the block */
1999
2000
    /* Set the shared heap header's file context for this operation */
2001
0
    hdr->f = (H5F_t *)f;
2002
2003
0
    assert(hdr);
2004
0
    assert(hdr->cache_info.type == H5AC_FHEAP_HDR);
2005
2006
0
    if (dblock->parent) {
2007
        /* this is the common case, in which the direct block is the child
2008
         * of an indirect block.  Set up the convenience variables we will
2009
         * need if the address and/or compressed size of the on disk image
2010
         * of the direct block changes, and do some sanity checking in
2011
         * passing.
2012
         */
2013
0
        par_iblock = dblock->parent;
2014
0
        par_entry  = dblock->par_entry;
2015
2016
0
        assert(par_iblock->cache_info.type == H5AC_FHEAP_IBLOCK);
2017
0
        assert(H5_addr_eq(par_iblock->ents[par_entry].addr, addr));
2018
0
    } /* end if */
2019
0
    else {
2020
        /* the direct block is a root direct block -- just set par_iblock
2021
         * to NULL, as the field will not be used.
2022
         */
2023
0
        par_iblock = NULL;
2024
0
    } /* end else */
2025
2026
0
    at_tmp_addr = H5F_IS_TMP_ADDR(f, addr);
2027
2028
    /* Begin by preping the direct block to be written to disk.  Do
2029
     * this by writing the correct magic number, the dblock version,
2030
     * the address of the header, the offset of the block in the heap,
2031
     * and the checksum at the beginning of the block.
2032
     */
2033
2034
0
    assert(dblock->blk);
2035
0
    image = dblock->blk;
2036
2037
    /* Magic number */
2038
0
    H5MM_memcpy(image, H5HF_DBLOCK_MAGIC, (size_t)H5_SIZEOF_MAGIC);
2039
0
    image += H5_SIZEOF_MAGIC;
2040
2041
    /* Version # */
2042
0
    *image++ = H5HF_DBLOCK_VERSION;
2043
2044
    /* Address of heap header for heap which owns this block */
2045
0
    H5F_addr_encode(f, &image, hdr->heap_addr);
2046
2047
    /* Offset of block in heap */
2048
0
    UINT64ENCODE_VAR(image, dblock->block_off, hdr->heap_off_size);
2049
2050
    /* Metadata checksum */
2051
0
    if (hdr->checksum_dblocks) {
2052
0
        uint32_t metadata_chksum; /* Computed metadata checksum value */
2053
2054
        /* Clear the checksum field, to compute the checksum */
2055
0
        memset(image, 0, (size_t)H5HF_SIZEOF_CHKSUM);
2056
2057
        /* Compute checksum on entire direct block */
2058
0
        metadata_chksum = H5_checksum_metadata(dblock->blk, dblock->size, 0);
2059
2060
        /* Metadata checksum */
2061
0
        UINT32ENCODE(image, metadata_chksum);
2062
0
    } /* end if */
2063
2064
    /* at this point, dblock->blk should point to an uncompressed image of
2065
     * the direct block.  If I/O filters are not enabled, this image should
2066
     * be ready to hand off to the metadata cache.
2067
     */
2068
2069
    /* Sanity check */
2070
0
    assert((size_t)(image - dblock->blk) == (size_t)H5HF_MAN_ABS_DIRECT_OVERHEAD(hdr));
2071
2072
    /* If I/O filters are enabled on this heap, we must run the direct block
2073
     * image through the filters to obtain the image that we will hand off
2074
     * to the metadata cache.
2075
     */
2076
2077
    /* Check for I/O filters on this heap */
2078
0
    if (hdr->filter_len > 0) {
2079
0
        H5Z_cb_t filter_cb;       /* Filter callback structure */
2080
0
        size_t   nbytes;          /* Number of bytes used */
2081
0
        unsigned filter_mask = 0; /* Filter mask for block */
2082
2083
        /* Initialize the filter callback struct */
2084
0
        filter_cb.op_data = NULL;
2085
0
        filter_cb.func    = NULL; /* no callback function when failed */
2086
2087
        /* Allocate buffer to perform I/O filtering on */
2088
0
        write_size = dblock->size;
2089
0
        if (NULL == (write_buf = H5MM_malloc(write_size)))
2090
0
            HGOTO_ERROR(H5E_HEAP, H5E_NOSPACE, FAIL, "memory allocation failed for pipeline buffer");
2091
2092
        /* Copy the direct block's image into the buffer to compress */
2093
0
        H5MM_memcpy(write_buf, dblock->blk, write_size);
2094
2095
        /* Push direct block data through I/O filter pipeline */
2096
0
        nbytes = write_size;
2097
0
        if (H5Z_pipeline(&(hdr->pline), 0, &filter_mask, H5Z_ENABLE_EDC, filter_cb, &nbytes, &write_size,
2098
0
                         &write_buf) < 0)
2099
0
            HGOTO_ERROR(H5E_HEAP, H5E_WRITEERROR, FAIL, "output pipeline failed");
2100
2101
        /* Use the compressed number of bytes as the size to write */
2102
0
        write_size = nbytes;
2103
2104
        /* If the size and/or location of the on disk image of the
2105
         * direct block changes, we must touch up its parent to reflect
2106
         * these changes.  Do this differently depending on whether the
2107
         * direct block's parent is an indirect block or (rarely) the
2108
         * fractal heap header.  In this case, the direct block is known
2109
         * as a root direct block.
2110
         */
2111
2112
        /* Check for root direct block */
2113
0
        if (dblock->parent == NULL) {
2114
0
            bool hdr_changed = false; /* Whether the header info changed */
2115
2116
            /* Sanity check */
2117
0
            assert(H5_addr_eq(hdr->man_dtable.table_addr, addr));
2118
0
            assert(hdr->pline_root_direct_size > 0);
2119
2120
            /* Check if the filter mask changed */
2121
0
            if (hdr->pline_root_direct_filter_mask != filter_mask) {
2122
0
                hdr->pline_root_direct_filter_mask = filter_mask;
2123
0
                hdr_changed                        = true;
2124
0
            } /* end if */
2125
2126
            /* verify that the cache's last record of the compressed
2127
             * size matches the heap's last record.  This value will
2128
             * likely change shortly.
2129
             */
2130
0
            assert(len == hdr->pline_root_direct_size);
2131
2132
            /* Check if we need to re-size the block on disk */
2133
0
            if (hdr->pline_root_direct_size != write_size || at_tmp_addr) {
2134
                /* Check if the direct block is NOT currently allocated
2135
                 * in temp. file space
2136
                 *
2137
                 * (temp. file space does not need to be freed)
2138
                 */
2139
0
                if (!at_tmp_addr)
2140
                    /* Release direct block's current disk space */
2141
0
                    if (H5MF_xfree(f, H5FD_MEM_FHEAP_DBLOCK, addr, (hsize_t)hdr->pline_root_direct_size) < 0)
2142
0
                        HGOTO_ERROR(H5E_HEAP, H5E_CANTFREE, FAIL, "unable to free fractal heap direct block");
2143
2144
                /* Allocate space for the compressed direct block */
2145
0
                if (HADDR_UNDEF ==
2146
0
                    (dblock_addr = H5MF_alloc((H5F_t *)f, H5FD_MEM_FHEAP_DBLOCK, (hsize_t)write_size)))
2147
0
                    HGOTO_ERROR(H5E_HEAP, H5E_NOSPACE, FAIL,
2148
0
                                "file allocation failed for fractal heap direct block");
2149
2150
                /* Update information about compressed direct block's
2151
                 * location & size
2152
                 */
2153
0
                assert(hdr->man_dtable.table_addr == addr);
2154
0
                assert(hdr->pline_root_direct_size == len);
2155
0
                hdr->man_dtable.table_addr  = dblock_addr;
2156
0
                hdr->pline_root_direct_size = write_size;
2157
2158
                /* Note that heap header was modified */
2159
0
                hdr_changed = true;
2160
0
            } /* end if */
2161
2162
            /* Check if heap header was modified */
2163
0
            if (hdr_changed)
2164
0
                if (H5HF__hdr_dirty(hdr) < 0)
2165
0
                    HGOTO_ERROR(H5E_HEAP, H5E_CANTDIRTY, FAIL, "can't mark heap header as dirty");
2166
0
        }                             /* end if */
2167
0
        else {                        /* the direct block's parent is an indirect block */
2168
0
            bool par_changed = false; /* Whether the parent's infochanged */
2169
2170
            /* Sanity check */
2171
0
            assert(par_iblock);
2172
0
            assert(par_iblock->filt_ents[par_entry].size > 0);
2173
2174
            /* Check if the filter mask changed */
2175
0
            if (par_iblock->filt_ents[par_entry].filter_mask != filter_mask) {
2176
0
                par_iblock->filt_ents[par_entry].filter_mask = filter_mask;
2177
0
                par_changed                                  = true;
2178
0
            } /* end if */
2179
2180
            /* verify that the cache's last record of the compressed
2181
             * size matches the heap's last record.  This value will
2182
             * likely change shortly.
2183
             */
2184
0
            assert(len == par_iblock->filt_ents[par_entry].size);
2185
2186
            /* Check if we need to re-size the block on disk */
2187
0
            if (par_iblock->filt_ents[par_entry].size != write_size || at_tmp_addr) {
2188
                /* Check if the direct block is NOT currently allocated
2189
                 * in temp. file space
2190
                 *
2191
                 * (temp. file space does not need to be freed)
2192
                 */
2193
0
                if (!at_tmp_addr)
2194
                    /* Release direct block's current disk space */
2195
0
                    if (H5MF_xfree(f, H5FD_MEM_FHEAP_DBLOCK, addr,
2196
0
                                   (hsize_t)par_iblock->filt_ents[par_entry].size) < 0)
2197
0
                        HGOTO_ERROR(H5E_HEAP, H5E_CANTFREE, FAIL, "unable to free fractal heap direct block");
2198
2199
                /* Allocate space for the compressed direct block */
2200
0
                if (HADDR_UNDEF ==
2201
0
                    (dblock_addr = H5MF_alloc((H5F_t *)f, H5FD_MEM_FHEAP_DBLOCK, (hsize_t)write_size)))
2202
0
                    HGOTO_ERROR(H5E_HEAP, H5E_NOSPACE, FAIL,
2203
0
                                "file allocation failed for fractal heap direct block");
2204
2205
                /* Update information about compressed direct block's
2206
                 * location & size
2207
                 */
2208
0
                assert(par_iblock->ents[par_entry].addr == addr);
2209
0
                assert(par_iblock->filt_ents[par_entry].size == len);
2210
0
                par_iblock->ents[par_entry].addr      = dblock_addr;
2211
0
                par_iblock->filt_ents[par_entry].size = write_size;
2212
2213
                /* Note that parent was modified */
2214
0
                par_changed = true;
2215
0
            } /* end if */
2216
2217
            /* Check if parent was modified */
2218
0
            if (par_changed)
2219
0
                if (H5HF__iblock_dirty(par_iblock) < 0)
2220
0
                    HGOTO_ERROR(H5E_HEAP, H5E_CANTDIRTY, FAIL, "can't mark heap header as dirty");
2221
0
        } /* end else */
2222
0
    }     /* end if */
2223
0
    else {
2224
        /* I/O filters are not enabled -- thus all we need to do is check to
2225
         * see if the direct block is in temporary (AKA imaginary) file
2226
         * space, and move it to real file space if it is.
2227
         *
2228
         * As in the I/O filters case above, we will have to touch up the
2229
         * direct blocks parent if the direct block is relocated.
2230
         *
2231
         * Recall that temporary file space need not be freed, which
2232
         * simplifies matters slightly.
2233
         */
2234
0
        write_buf  = dblock->blk;
2235
0
        write_size = dblock->size;
2236
2237
        /* Check to see if we must re-allocate direct block from 'temp.'
2238
         * to 'normal' file space
2239
         */
2240
0
        if (at_tmp_addr) {
2241
            /* Allocate 'normal' space for the direct block */
2242
0
            if (HADDR_UNDEF ==
2243
0
                (dblock_addr = H5MF_alloc((H5F_t *)f, H5FD_MEM_FHEAP_DBLOCK, (hsize_t)write_size)))
2244
0
                HGOTO_ERROR(H5E_HEAP, H5E_NOSPACE, FAIL,
2245
0
                            "file allocation failed for fractal heap direct block");
2246
2247
            /* Check for root direct block */
2248
0
            if (NULL == dblock->parent) {
2249
                /* Sanity checks */
2250
0
                assert(H5_addr_eq(hdr->man_dtable.table_addr, addr));
2251
0
                assert(!H5_addr_eq(hdr->man_dtable.table_addr, dblock_addr));
2252
2253
                /* Update information about direct block's location */
2254
0
                hdr->man_dtable.table_addr = dblock_addr;
2255
2256
                /* Mark that heap header was modified */
2257
0
                if (H5HF__hdr_dirty(hdr) < 0)
2258
0
                    HGOTO_ERROR(H5E_HEAP, H5E_CANTDIRTY, FAIL, "can't mark heap header as dirty");
2259
0
            }      /* end if */
2260
0
            else { /* the direct block's parent is an indirect block */
2261
                /* Sanity checks */
2262
0
                assert(par_iblock);
2263
0
                assert(par_iblock->ents);
2264
0
                assert(H5_addr_eq(par_iblock->ents[par_entry].addr, addr));
2265
0
                assert(!H5_addr_eq(par_iblock->ents[par_entry].addr, dblock_addr));
2266
2267
                /* Update information about direct block's location */
2268
0
                par_iblock->ents[par_entry].addr = dblock_addr;
2269
2270
                /* Mark that parent was modified */
2271
0
                if (H5HF__iblock_dirty(par_iblock) < 0)
2272
0
                    HGOTO_ERROR(H5E_HEAP, H5E_CANTDIRTY, FAIL, "can't mark heap header as dirty");
2273
0
            } /* end else */
2274
0
        }     /* end if */
2275
0
    }         /* end else */
2276
2277
    /* At this point, write_buf points to a buffer containing the image
2278
     * of the direct block that is ready to copy into the image buffer,
2279
     * and write_size contains the length of this buffer.
2280
     *
2281
     * Also, if image size or address has changed, the direct block's
2282
     * parent has been modified to reflect the change.
2283
     *
2284
     * Now, make note of the pointer and length of the above buffer for
2285
     * use by the serialize function.
2286
     */
2287
0
    dblock->write_buf  = (uint8_t *)write_buf;
2288
0
    dblock->write_size = write_size;
2289
2290
    /* finally, pass data back to the metadata cache as appropriate */
2291
0
    if (!H5_addr_eq(addr, dblock_addr)) {
2292
0
        dblock_flags |= H5AC__SERIALIZE_MOVED_FLAG;
2293
0
        *new_addr = dblock_addr;
2294
0
    } /* end if */
2295
2296
0
    if ((hdr->filter_len > 0) && (len != write_size)) {
2297
0
        dblock_flags |= H5AC__SERIALIZE_RESIZED_FLAG;
2298
0
        *new_len = write_size;
2299
0
    } /* end if */
2300
2301
0
    *flags = dblock_flags;
2302
2303
    /* final sanity check */
2304
0
    assert(dblock->write_buf);
2305
0
    assert(dblock->write_size > 0);
2306
2307
0
done:
2308
    /* discard the write buf if we have an error */
2309
0
    if (write_buf && (write_buf != dblock->blk) && (dblock->write_buf == NULL))
2310
0
        H5MM_xfree(write_buf);
2311
2312
0
    FUNC_LEAVE_NOAPI(ret_value)
2313
0
} /* end H5HF__cache_dblock_pre_serialize() */
2314
2315
/*-------------------------------------------------------------------------
2316
 * Function:  H5HF__cache_dblock_serialize
2317
 *
2318
 * Purpose: In principle, this function is supposed to construct the on
2319
 *    disk image of the direct block, and place that image in the
2320
 *    image buffer provided by the metadata cache.
2321
 *
2322
 *    However, since there are cases in which the pre_serialize
2323
 *    function has to construct the on disk image to determine its size
2324
 *    and address, this function simply copies the image prepared by
2325
 *    the pre-serialize function into the supplied image buffer, and
2326
 *    discards a buffer if necessary.
2327
 *
2328
 * Return:  Success:  SUCCEED
2329
 *    Failure:  FAIL
2330
 *
2331
 *-------------------------------------------------------------------------
2332
 */
2333
static herr_t
2334
H5HF__cache_dblock_serialize(const H5F_t H5_ATTR_NDEBUG_UNUSED *f, void *image,
2335
                             size_t H5_ATTR_NDEBUG_UNUSED len, void *_thing)
2336
0
{
2337
0
    H5HF_direct_t *dblock    = (H5HF_direct_t *)_thing; /* Direct block info */
2338
0
    herr_t         ret_value = SUCCEED;                 /* Return value */
2339
2340
0
    FUNC_ENTER_PACKAGE_NOERR
2341
2342
    /* Sanity checks */
2343
0
    assert(f);
2344
0
    assert(image);
2345
0
    assert(len > 0);
2346
0
    assert(dblock);
2347
0
    assert(dblock->cache_info.type == H5AC_FHEAP_DBLOCK);
2348
0
    assert((dblock->blk != dblock->write_buf) || (dblock->cache_info.size == dblock->size));
2349
0
    assert(dblock->write_buf);
2350
0
    assert(dblock->write_size > 0);
2351
0
    assert((dblock->blk != dblock->write_buf) || (dblock->write_size == dblock->size));
2352
0
    assert(dblock->write_size == len);
2353
2354
    /* Copy the image from *(dblock->write_buf) to *image */
2355
0
    H5MM_memcpy(image, dblock->write_buf, dblock->write_size);
2356
2357
    /* Free *(dblock->write_buf) if it was allocated by the
2358
     * pre-serialize function
2359
     */
2360
0
    if (dblock->write_buf != dblock->blk)
2361
0
        H5MM_xfree(dblock->write_buf);
2362
2363
    /* Reset the write_buf and write_size fields */
2364
0
    dblock->write_buf  = NULL;
2365
0
    dblock->write_size = 0;
2366
2367
0
    FUNC_LEAVE_NOAPI(ret_value)
2368
0
} /* end H5HF__cache_dblock_serialize() */
2369
2370
/*-------------------------------------------------------------------------
2371
 * Function:  H5HF__cache_dblock_notify
2372
 *
2373
 * Purpose: Setup / takedown flush dependencies as direct blocks
2374
 *    are loaded / inserted and evicted from the metadata cache.
2375
 *
2376
 * Return:  Success:  SUCCEED
2377
 *    Failure:  FAIL
2378
 *
2379
 *-------------------------------------------------------------------------
2380
 */
2381
static herr_t
2382
H5HF__cache_dblock_notify(H5AC_notify_action_t action, void *_thing)
2383
0
{
2384
0
    H5HF_direct_t *dblock    = (H5HF_direct_t *)_thing; /* Fractal heap direct block */
2385
0
    herr_t         ret_value = SUCCEED;                 /* Return value */
2386
2387
0
    FUNC_ENTER_PACKAGE
2388
2389
    /* Sanity checks */
2390
0
    assert(dblock);
2391
0
    assert(dblock->cache_info.type == H5AC_FHEAP_DBLOCK);
2392
0
    assert(dblock->hdr);
2393
2394
0
    switch (action) {
2395
0
        case H5AC_NOTIFY_ACTION_AFTER_INSERT:
2396
0
        case H5AC_NOTIFY_ACTION_AFTER_LOAD:
2397
            /* Create flush dependency with parent, if there is one */
2398
0
            if (dblock->fd_parent)
2399
0
                if (H5AC_create_flush_dependency(dblock->fd_parent, dblock) < 0)
2400
0
                    HGOTO_ERROR(H5E_HEAP, H5E_CANTDEPEND, FAIL, "unable to create flush dependency");
2401
0
            break;
2402
2403
0
        case H5AC_NOTIFY_ACTION_AFTER_FLUSH:
2404
0
        case H5AC_NOTIFY_ACTION_ENTRY_DIRTIED:
2405
0
        case H5AC_NOTIFY_ACTION_ENTRY_CLEANED:
2406
0
        case H5AC_NOTIFY_ACTION_CHILD_DIRTIED:
2407
0
        case H5AC_NOTIFY_ACTION_CHILD_CLEANED:
2408
0
        case H5AC_NOTIFY_ACTION_CHILD_UNSERIALIZED:
2409
0
        case H5AC_NOTIFY_ACTION_CHILD_SERIALIZED:
2410
            /* do nothing */
2411
0
            break;
2412
2413
0
        case H5AC_NOTIFY_ACTION_BEFORE_EVICT:
2414
0
            if (dblock->fd_parent) {
2415
                /* Destroy flush dependency with parent */
2416
0
                if (H5AC_destroy_flush_dependency(dblock->fd_parent, dblock) < 0)
2417
0
                    HGOTO_ERROR(H5E_HEAP, H5E_CANTUNDEPEND, FAIL, "unable to destroy flush dependency");
2418
0
                dblock->fd_parent = NULL;
2419
0
            } /* end if */
2420
0
            break;
2421
2422
0
        default:
2423
0
            HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "unknown action from metadata cache");
2424
0
            break;
2425
0
    } /* end switch */
2426
2427
0
done:
2428
0
    FUNC_LEAVE_NOAPI(ret_value)
2429
0
} /* end H5HF__cache_dblock_notify() */
2430
2431
/*-------------------------------------------------------------------------
2432
 * Function:  H5HF__cache_dblock_free_icr
2433
 *
2434
 * Purpose: Free the in core memory allocated to the supplied direct
2435
 *    block.
2436
 *
2437
 * Return:  Success:  SUCCEED
2438
 *    Failure:  FAIL
2439
 *
2440
 *-------------------------------------------------------------------------
2441
 */
2442
static herr_t
2443
H5HF__cache_dblock_free_icr(void *_thing)
2444
0
{
2445
0
    H5HF_direct_t *dblock    = (H5HF_direct_t *)_thing; /* Fractal heap direct block */
2446
0
    herr_t         ret_value = SUCCEED;                 /* Return value */
2447
2448
0
    FUNC_ENTER_PACKAGE
2449
2450
    /* Sanity checks */
2451
0
    assert(dblock);
2452
0
    assert(dblock->cache_info.type == H5AC_FHEAP_DBLOCK);
2453
2454
    /* Destroy fractal heap direct block */
2455
0
    if (H5HF__man_dblock_dest(dblock) < 0)
2456
0
        HGOTO_ERROR(H5E_HEAP, H5E_CANTFREE, FAIL, "unable to destroy fractal heap direct block");
2457
2458
0
done:
2459
0
    FUNC_LEAVE_NOAPI(ret_value)
2460
0
} /* end H5HF__cache_dblock_free_icr() */
2461
2462
/*-------------------------------------------------------------------------
2463
 * Function:  H5HF__cache_dblock_fsf_size
2464
 *
2465
 * Purpose:     Tell the metadata cache the actual amount of file space
2466
 *              to free when a dblock entry is destroyed with the free
2467
 *              file space flag set.
2468
 *
2469
 * Return:  Success:  SUCCEED
2470
 *    Failure:  FAIL
2471
 *
2472
 *-------------------------------------------------------------------------
2473
 */
2474
static herr_t
2475
H5HF__cache_dblock_fsf_size(const void *_thing, hsize_t *fsf_size)
2476
0
{
2477
0
    const H5HF_direct_t *dblock = (const H5HF_direct_t *)_thing; /* Fractal heap direct block */
2478
2479
0
    FUNC_ENTER_PACKAGE_NOERR
2480
2481
    /* Sanity checks */
2482
0
    assert(dblock);
2483
0
    assert(dblock->cache_info.type == H5AC_FHEAP_DBLOCK);
2484
0
    assert(dblock->file_size > 0);
2485
0
    assert(fsf_size);
2486
2487
    /* Set free space in file size */
2488
0
    *fsf_size = dblock->file_size;
2489
2490
0
    FUNC_LEAVE_NOAPI(SUCCEED)
2491
0
} /* end H5HF__cache_dblock_fsf_size() */
2492
2493
/*------------------------------------------------------------------------
2494
 * Function:  H5HF__cache_verify_hdr_descendants_clean
2495
 *
2496
 * Purpose: Sanity checking routine that verifies that all indirect
2497
 *    and direct blocks that are descendants of the supplied
2498
 *    instance of H5HF_hdr_t are clean.  Set *clean to
2499
 *    true if this is the case, and to false otherwise.
2500
 *
2501
 *    Update -- 8/24/15
2502
 *
2503
 *    With the advent of the metadata cache image feature, it is
2504
 *    possible for the pre-serialize and serialize calls to be
2505
 *    invoked outside of a flush.  While this serialization
2506
 *    observes flush dependencies for the order of serialization,
2507
 *    the entries are not written to disk, and hence dirty entries
2508
 *    remain dirty.
2509
 *
2510
 *    To address this, updated the sanity checks in this function
2511
 *    to treat entries whose images are up to date as clean if
2512
 *    a cache serialization is in progress.
2513
 *
2514
 *    Update -- 9/29/16
2515
 *
2516
 *    The implementation of flush dependencies has been changed.
2517
 *    Prior to this change, a flush dependency parent could be
2518
 *    flushed if and only if all its flush dependency descendants
2519
 *    were clean.  In the new definition, a flush dependency
2520
 *    parent can be flushed if all its immediate flush dependency
2521
 *    children are clean, regardless of any other dirty
2522
 *    descendants.
2523
 *
2524
 *    Further, metadata cache entries are now allowed to have
2525
 *    multiple flush dependency parents.
2526
 *
2527
 *    This means that the fractal heap is no longer necessarily
2528
 *    flushed from the bottom up.
2529
 *
2530
 *    For example, it is now possible for a dirty fractal heap
2531
 *    header to be flushed before a dirty dblock, as long as the
2532
 *    there in an intervening iblock, and the header has no
2533
 *    dirty immediate flush dependency children.
2534
 *
2535
 *    Also, I gather that under some circumstances, a dblock
2536
 *    will be direct a flush dependency child both of the iblock
2537
 *    that points to it, and of the fractal heap header.
2538
 *
2539
 *    As a result of these changes, the functionality of these
2540
 *    sanity checking routines has been modified significantly.
2541
 *    Instead of scanning the fractal heap from a starting point
2542
 *    down, and verifying that there were no dirty entries, the
2543
 *    functions now scan downward from the starting point and
2544
 *    verify that there are no dirty flush dependency children
2545
 *    of the specified flush dependency parent.  In passing,
2546
 *    they also walk the data structure, and verify it.
2547
 *
2548
 *
2549
 * Return:  Non-negative on success/Negative on failure
2550
 *
2551
 *-------------------------------------------------------------------------
2552
 */
2553
#ifndef NDEBUG
2554
static herr_t
2555
H5HF__cache_verify_hdr_descendants_clean(H5F_t *f, H5HF_hdr_t *hdr, bool *fd_clean, bool *clean)
2556
{
2557
    bool     fd_exists = false;    /* whether flush dependency exists. */
2558
    haddr_t  hdr_addr;             /* Address of header */
2559
    unsigned hdr_status = 0;       /* Header cache entry status */
2560
    herr_t   ret_value  = SUCCEED; /* Return value */
2561
2562
    FUNC_ENTER_PACKAGE
2563
2564
    /* Sanity checks */
2565
    assert(f);
2566
    assert(hdr);
2567
    assert(hdr->cache_info.type == H5AC_FHEAP_HDR);
2568
    assert(fd_clean);
2569
    assert(clean);
2570
    hdr_addr = hdr->cache_info.addr;
2571
    assert(hdr_addr == hdr->heap_addr);
2572
2573
    if (H5AC_get_entry_status(f, hdr_addr, &hdr_status) < 0)
2574
        HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't get hdr status");
2575
    assert(hdr_status & H5AC_ES__IN_CACHE);
2576
2577
    /* We have three basic scenarios we have to deal with:
2578
     *
2579
     * The first, and most common case, is that there is a root iblock.
2580
     * In this case we need to verify that the root iblock and all its
2581
     * children are clean.
2582
     *
2583
     * The second, and much less common case, is that in which the
2584
     * the fractal heap contains only one direct block, which is
2585
     * pointed to by hdr->man_dtable.table_addr.  In this case, all we
2586
     * need to do is verify that the root direct block is clean.
2587
     *
2588
     * Finally, it is possible that the fractal heap is empty, and
2589
     * has neither a root indirect block nor a root direct block.
2590
     * In this case, we have nothing to do.
2591
     */
2592
2593
    /* There are two ways in which we can arrive at the first scenario.
2594
     *
2595
     * By far the most common is when hdr->root_iblock contains a pointer
2596
     * to the root iblock -- in this case the root iblock is almost certainly
2597
     * pinned, although we can't count on that.
2598
     *
2599
     * However, it is also possible that there is a root iblock that
2600
     * is no longer pointed to by the header.  In this case, the on
2601
     * disk address of the iblock will be in hdr->man_dtable.table_addr
2602
     * and hdr->man_dtable.curr_root_rows will contain a positive value.
2603
     *
2604
     * Since the former case is far and away the most common, we don't
2605
     * worry too much about efficiency in the second case.
2606
     */
2607
    if (hdr->root_iblock ||
2608
        ((hdr->man_dtable.curr_root_rows > 0) && (HADDR_UNDEF != hdr->man_dtable.table_addr))) {
2609
        H5HF_indirect_t *root_iblock = hdr->root_iblock;
2610
        haddr_t          root_iblock_addr;
2611
        unsigned         root_iblock_status = 0;
2612
        bool             root_iblock_in_cache;
2613
2614
        /* make note of the on disk address of the root iblock */
2615
        if (root_iblock == NULL)
2616
            /* hdr->man_dtable.table_addr must contain address of root
2617
             * iblock.  Check to see if it is in cache.  If it is,
2618
             * protect it and put its address in root_iblock.
2619
             */
2620
            root_iblock_addr = hdr->man_dtable.table_addr;
2621
        else
2622
            root_iblock_addr = root_iblock->addr;
2623
2624
        /* get the status of the root iblock */
2625
        assert(root_iblock_addr != HADDR_UNDEF);
2626
        if (H5AC_get_entry_status(f, root_iblock_addr, &root_iblock_status) < 0)
2627
            HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't get root iblock status");
2628
2629
        root_iblock_in_cache = ((root_iblock_status & H5AC_ES__IN_CACHE) != 0);
2630
        assert(root_iblock_in_cache || (root_iblock == NULL));
2631
2632
        if (!root_iblock_in_cache) { /* we are done */
2633
            *clean    = true;
2634
            *fd_clean = true;
2635
        } /* end if */
2636
        else if ((root_iblock_status & H5AC_ES__IS_DIRTY) &&
2637
                 (((root_iblock_status & H5AC_ES__IMAGE_IS_UP_TO_DATE) == 0) ||
2638
                  (!H5AC_get_serialization_in_progress(f)))) {
2639
            *clean = false;
2640
2641
            /* verify that a flush dependency exists between the header and
2642
             * the root inode.
2643
             */
2644
            if (H5AC_flush_dependency_exists(f, hdr->heap_addr, root_iblock_addr, &fd_exists) < 0)
2645
                HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't check flush dependency");
2646
            assert(fd_exists);
2647
2648
            *fd_clean = false;
2649
        }      /* end else-if */
2650
        else { /* must examine children */
2651
            bool unprotect_root_iblock = false;
2652
2653
            /* At this point, the root iblock may be pinned, protected,
2654
             * both, or neither, and we may or may not have a pointer
2655
             * to root iblock in memory.
2656
             *
2657
             * Before we call H5HF__cache_verify_iblock_descendants_clean(),
2658
             * we must ensure that the root iblock is either pinned or
2659
             * protected or both, and that we have a pointer to it.
2660
             * Do this as follows:
2661
             */
2662
            if (root_iblock == NULL) { /* we don't have ptr to root iblock */
2663
                if (0 == (root_iblock_status & H5AC_ES__IS_PROTECTED)) {
2664
                    /* just protect the root iblock -- this will give us
2665
                     * the pointer we need to proceed, and ensure that
2666
                     * it is locked into the metadata cache for the
2667
                     * duration.
2668
                     *
2669
                     * Note that the udata is only used in the load callback.
2670
                     * While the fractal heap makes heavy use of the udata
2671
                     * in this case, since we know that the entry is in cache,
2672
                     * we can pass NULL udata.
2673
                     *
2674
                     * The tag specified in the API context we received
2675
                     * as a parameter (via API context) may not be correct.
2676
                     * Grab the (hopefully) correct tag from the header,
2677
                     * and load it into the API context via the H5_BEGIN_TAG and
2678
                     * H5_END_TAG macros.  Note that any error bracked by
2679
                     * these macros must be reported with HGOTO_ERROR_TAG.
2680
                     */
2681
                    H5_BEGIN_TAG(hdr->heap_addr)
2682
2683
                    if (NULL == (root_iblock = (H5HF_indirect_t *)H5AC_protect(
2684
                                     f, H5AC_FHEAP_IBLOCK, root_iblock_addr, NULL, H5AC__READ_ONLY_FLAG)))
2685
                        HGOTO_ERROR_TAG(H5E_HEAP, H5E_CANTPROTECT, FAIL, "H5AC_protect() failed.");
2686
2687
                    H5_END_TAG
2688
2689
                    unprotect_root_iblock = true;
2690
                } /* end if */
2691
                else {
2692
                    /* the root iblock is protected, and we have no
2693
                     * legitimate way of getting a pointer to it.
2694
                     *
2695
                     * We square this circle by using the
2696
                     * H5AC_get_entry_ptr_from_addr() to get the needed
2697
                     * pointer.
2698
                     *
2699
                     * WARNING: This call should be used only in debugging
2700
                     *          routines, and it should be avoided there when
2701
                     *          possible.
2702
                     *
2703
                     *          Further, if we ever multi-thread the cache,
2704
                     *          this routine will have to be either discarded
2705
                     *          or heavily re-worked.
2706
                     *
2707
                     *          Finally, keep in mind that the entry whose
2708
                     *          pointer is obtained in this fashion may not
2709
                     *          be in a stable state.
2710
                     *
2711
                     * Assuming that the flush dependency code is working
2712
                     * as it should, the only reason for the root iblock to
2713
                     * be unpinned is if none of its children are in cache.
2714
                     * This unfortunately means that if it is protected and
2715
                     * not pinned, the fractal heap is in the process of loading
2716
                     * or inserting one of its children.  The obvious
2717
                     * implication is that there is a significant chance that
2718
                     * the root iblock is in an unstable state.
2719
                     *
2720
                     * All this suggests that using
2721
                     * H5AC_get_entry_ptr_from_addr() to obtain the pointer
2722
                     * to the protected root iblock is questionable here.
2723
                     * However, since this is test/debugging code, I expect
2724
                     * that we will use this approach until it causes problems,
2725
                     *  or we think of a better way.
2726
                     */
2727
                    if (H5AC_get_entry_ptr_from_addr(f, root_iblock_addr, (void **)(&root_iblock)) < 0)
2728
                        HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "H5AC_get_entry_ptr_from_addr() failed.");
2729
                    assert(root_iblock);
2730
                }  /* end else */
2731
            }      /* end if */
2732
            else { /* root_iblock != NULL */
2733
                   /* we have the pointer to the root iblock.  Protect it
2734
                    * if it is neither pinned nor protected -- otherwise we
2735
                    * are ready to go.
2736
                    */
2737
                H5HF_indirect_t *iblock = NULL;
2738
2739
                if (((root_iblock_status & H5AC_ES__IS_PINNED) == 0) &&
2740
                    ((root_iblock_status & H5AC_ES__IS_PROTECTED) == 0)) {
2741
                    /* the root iblock is neither pinned nor protected -- hence
2742
                     * we must protect it before we proceed
2743
                     *
2744
                     * Note that the udata is only used in the load callback.
2745
                     * While the fractal heap makes heavy use of the udata
2746
                     * in this case, since we know that the entry is in cache,
2747
                     * we can pass NULL udata.
2748
                     *
2749
                     * The tag associated specified in the API context we received
2750
                     * as a parameter (via API context) may not be correct.
2751
                     * Grab the (hopefully) correct tag from the header,
2752
                     * and load it into the API context via the H5_BEGIN_TAG and
2753
                     * H5_END_TAG macros.  Note that any error bracked by
2754
                     * these macros must be reported with HGOTO_ERROR_TAG.
2755
                     */
2756
                    H5_BEGIN_TAG(hdr->heap_addr)
2757
2758
                    if (NULL == (iblock = (H5HF_indirect_t *)H5AC_protect(
2759
                                     f, H5AC_FHEAP_IBLOCK, root_iblock_addr, NULL, H5AC__READ_ONLY_FLAG)))
2760
                        HGOTO_ERROR_TAG(H5E_HEAP, H5E_CANTPROTECT, FAIL, "H5AC_protect() failed.");
2761
2762
                    H5_END_TAG
2763
2764
                    unprotect_root_iblock = true;
2765
                    assert(iblock == root_iblock);
2766
                } /* end if */
2767
            }     /* end else */
2768
2769
            /* at this point, one way or another, the root iblock is locked
2770
             * in memory for the duration of the call.  Do some sanity checks,
2771
             * and then call H5HF__cache_verify_iblock_descendants_clean().
2772
             */
2773
            assert(root_iblock->cache_info.type == H5AC_FHEAP_IBLOCK);
2774
2775
            if (H5HF__cache_verify_iblock_descendants_clean(f, hdr->heap_addr, root_iblock,
2776
                                                            &root_iblock_status, fd_clean, clean) < 0)
2777
                HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, "can't verify root iblock & descendants clean.");
2778
2779
            /* Unprotect the root indirect block if required */
2780
            if (unprotect_root_iblock) {
2781
                assert(root_iblock);
2782
                if (H5AC_unprotect(f, H5AC_FHEAP_IBLOCK, root_iblock_addr, root_iblock, H5AC__NO_FLAGS_SET) <
2783
                    0)
2784
                    HGOTO_ERROR(H5E_HEAP, H5E_CANTUNPROTECT, FAIL, "H5AC_unprotect() failed.");
2785
            } /* end if */
2786
        }     /* end else */
2787
    }         /* end if */
2788
    else if ((hdr->man_dtable.curr_root_rows == 0) && (HADDR_UNDEF != hdr->man_dtable.table_addr)) {
2789
        haddr_t  root_dblock_addr;
2790
        unsigned root_dblock_status = 0;
2791
        bool     in_cache;
2792
        bool     type_ok;
2793
2794
        /* this is scenario 2 -- we have a root dblock */
2795
        root_dblock_addr = hdr->man_dtable.table_addr;
2796
        if (H5AC_get_entry_status(f, root_dblock_addr, &root_dblock_status) < 0)
2797
            HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't get root dblock status");
2798
2799
        if (root_dblock_status & H5AC_ES__IN_CACHE) {
2800
            if (H5AC_verify_entry_type(f, root_dblock_addr, &H5AC_FHEAP_DBLOCK[0], &in_cache, &type_ok) < 0)
2801
                HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't check dblock type");
2802
            assert(in_cache);
2803
            if (!type_ok)
2804
                HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, "root dblock addr doesn't refer to a dblock?!?");
2805
2806
            /* If a root dblock is in cache, it must have a flush
2807
             * dependency relationship with the header, and it
2808
             * may not be the parent in any flush dependency
2809
             * relationship.
2810
             *
2811
             * We don't test this fully, but we will verify that
2812
             * the root iblock is a child in a flush dependency
2813
             * relationship with the header.
2814
             */
2815
            if (H5AC_flush_dependency_exists(f, hdr->heap_addr, root_dblock_addr, &fd_exists) < 0)
2816
                HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't check flush dependency");
2817
            if (!fd_exists)
2818
                HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, "root dblock is not a flush dep parent of header.");
2819
2820
            if (0 != (root_dblock_status & H5AC_ES__IS_FLUSH_DEP_PARENT))
2821
                HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, "root dblock in cache and is a flush dep parent.");
2822
2823
            *clean = !((root_dblock_status & H5AC_ES__IS_DIRTY) &&
2824
                       (((root_dblock_status & H5AC_ES__IMAGE_IS_UP_TO_DATE) == 0) ||
2825
                        (!H5AC_get_serialization_in_progress(f))));
2826
2827
            *fd_clean = *clean;
2828
        }      /* end if */
2829
        else { /* root dblock not in cache */
2830
            *fd_clean = true;
2831
            *clean    = true;
2832
        } /* end else */
2833
    }     /* end else-if */
2834
    else {
2835
        /* this is scenario 3 -- the fractal heap is empty, and we
2836
         * have nothing to do.
2837
         */
2838
        *fd_clean = true;
2839
        *clean    = true;
2840
    } /* end else */
2841
2842
done:
2843
    FUNC_LEAVE_NOAPI(ret_value)
2844
} /* H5HF__cache_verify_hdr_descendants_clean() */
2845
#endif /* NDEBUG */
2846
2847
/*------------------------------------------------------------------------
2848
 * Function:  H5HF__cache_verify_iblock_descendants_clean
2849
 *
2850
 * Purpose: Sanity checking routine that verifies that all indirect
2851
 *    and direct blocks that are descendants of the supplied
2852
 *    instance of H5HF_indirect_t are clean.  Set *clean
2853
 *    to true if this is the case, and to false otherwise.
2854
 *
2855
 *    In passing, the function also does a cursory check to
2856
 *    spot any obvious errors in the flush dependency setup.
2857
 *    If any problems are found, the function returns failure.
2858
 *    Note that these checks are not exhaustive, thus passing
2859
 *    them does not mean that the flush dependencies are
2860
 *    correct -- only that there is nothing obviously wrong
2861
 *    with them.
2862
 *
2863
 *    WARNING:  At its top level call, this function is
2864
 *    intended to be called from H5HF_cache_iblock_flush(),
2865
 *    and thus presumes that the supplied indirect block
2866
 *    is in cache.  Any other use of this function and
2867
 *    its descendants must insure that this assumption is
2868
 *    met.
2869
 *
2870
 *    Note that this function and
2871
 *    H5HF__cache_verify_descendant_iblocks_clean() are
2872
 *    recursive co-routines.
2873
 *
2874
 *    Update -- 9/29/16
2875
 *
2876
 *    The implementation of flush dependencies has been changed.
2877
 *    Prior to this change, a flush dependency parent could be
2878
 *    flushed if and only if all its flush dependency descendants
2879
 *    were clean.  In the new definition, a flush dependency
2880
 *    parent can be flushed if all its immediate flush dependency
2881
 *    children are clean, regardless of any other dirty
2882
 *    descendants.
2883
 *
2884
 *    Further, metadata cache entries are now allowed to have
2885
 *    multiple flush dependency parents.
2886
 *
2887
 *    This means that the fractal heap is no longer necessarily
2888
 *    flushed from the bottom up.
2889
 *
2890
 *    For example, it is now possible for a dirty fractal heap
2891
 *    header to be flushed before a dirty dblock, as long as the
2892
 *    there in an intervening iblock, and the header has no
2893
 *    dirty immediate flush dependency children.
2894
 *
2895
 *    Also, I gather that under some circumstances, a dblock
2896
 *    will be direct a flush dependency child both of the iblock
2897
 *    that points to it, and of the fractal heap header.
2898
 *
2899
 *    As a result of these changes, the functionality of these
2900
 *    sanity checking routines has been modified significantly.
2901
 *    Instead of scanning the fractal heap from a starting point
2902
 *    down, and verifying that there were no dirty entries, the
2903
 *    functions now scan downward from the starting point and
2904
 *    verify that there are no dirty flush dependency children
2905
 *    of the specified flush dependency parent.  In passing,
2906
 *    they also walk the data structure, and verify it.
2907
 *
2908
 * Return:  Non-negative on success/Negative on failure
2909
 *
2910
 *-------------------------------------------------------------------------
2911
 */
2912
#ifndef NDEBUG
2913
static herr_t
2914
H5HF__cache_verify_iblock_descendants_clean(H5F_t *f, haddr_t fd_parent_addr, H5HF_indirect_t *iblock,
2915
                                            unsigned *iblock_status, bool *fd_clean, bool *clean)
2916
{
2917
    bool   has_dblocks = false;
2918
    bool   has_iblocks = false;
2919
    herr_t ret_value   = SUCCEED; /* Return value */
2920
2921
    FUNC_ENTER_PACKAGE
2922
2923
    /* Sanity checks */
2924
    assert(f);
2925
    assert(H5_addr_defined(fd_parent_addr));
2926
    assert(iblock);
2927
    assert(iblock->cache_info.type == H5AC_FHEAP_IBLOCK);
2928
    assert(iblock_status);
2929
    assert(fd_clean);
2930
    assert(*fd_clean);
2931
    assert(clean); /* note that *clean need not be true */
2932
2933
    if ((*fd_clean) && H5HF__cache_verify_iblocks_dblocks_clean(f, fd_parent_addr, iblock, fd_clean, clean,
2934
                                                                &has_dblocks) < 0)
2935
        HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, "can't verify dblocks clean.");
2936
2937
    if ((*fd_clean) && H5HF__cache_verify_descendant_iblocks_clean(f, fd_parent_addr, iblock, fd_clean, clean,
2938
                                                                   &has_iblocks) < 0)
2939
        HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, "can't verify iblocks clean.");
2940
2941
    /* verify that flush dependency setup is plausible */
2942
    if (0 == (*iblock_status & H5AC_ES__IS_FLUSH_DEP_CHILD))
2943
        HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, "iblock is not a flush dep child.");
2944
    if (((has_dblocks || has_iblocks)) && (0 == (*iblock_status & H5AC_ES__IS_FLUSH_DEP_PARENT)))
2945
        HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, "iblock has children and is not a flush dep parent.");
2946
    if (((has_dblocks || has_iblocks)) && (0 == (*iblock_status & H5AC_ES__IS_PINNED)))
2947
        HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, "iblock has children and is not pinned.");
2948
2949
done:
2950
    FUNC_LEAVE_NOAPI(ret_value)
2951
} /* H5HF__cache_verify_iblock_descendants_clean() */
2952
#endif /* NDEBUG */
2953
2954
/*------------------------------------------------------------------------
2955
 * Function:  H5HF__cache_verify_iblocks_dblocks_clean
2956
 *
2957
 * Purpose: Sanity checking routine that attempts to verify that all
2958
 *    direct blocks pointed to by the supplied indirect block
2959
 *    are either clean, or not in the cache.
2960
 *
2961
 *    In passing, the function also does a cursory check to
2962
 *    spot any obvious errors in the flush dependency setup.
2963
 *    If any problems are found, the function returns failure.
2964
 *    Note that these checks are not exhaustive, thus passing
2965
 *    them does not mean that the flush dependencies are
2966
 *    correct -- only that there is nothing obviously wrong
2967
 *    with them.
2968
 *
2969
 *    WARNING:  This function presumes that the supplied
2970
 *    iblock is in the cache, and will not be removed
2971
 *    during the call.  Caller must ensure that this is
2972
 *    the case before the call.
2973
 *
2974
 *      Update -- 8/24/15
2975
 *
2976
 *      With the advent of the metadata cache image feature, it is
2977
 *      possible for the pre-serialize and serialize calls to be
2978
 *      invoked outside of a flush.  While this serialization
2979
 *      observes flush dependencies for the order of serialization,
2980
 *      the entries are not written to disk, and hence dirty entries
2981
 *      remain dirty.
2982
 *
2983
 *      To address this, updated the sanity checks in this function
2984
 *      to treat entries whose images are up to date as clean if
2985
 *      a cache serialization is in progress.
2986
 *
2987
 *    Update -- 9/29/16
2988
 *
2989
 *    The implementation of flush dependencies has been changed.
2990
 *    Prior to this change, a flush dependency parent could be
2991
 *    flushed if and only if all its flush dependency descendants
2992
 *    were clean.  In the new definition, a flush dependency
2993
 *    parent can be flushed if all its immediate flush dependency
2994
 *    children are clean, regardless of any other dirty
2995
 *    descendants.
2996
 *
2997
 *    Further, metadata cache entries are now allowed to have
2998
 *    multiple flush dependency parents.
2999
 *
3000
 *    This means that the fractal heap is no longer necessarily
3001
 *    flushed from the bottom up.
3002
 *
3003
 *    For example, it is now possible for a dirty fractal heap
3004
 *    header to be flushed before a dirty dblock, as long as the
3005
 *    there in an intervening iblock, and the header has no
3006
 *    dirty immediate flush dependency children.
3007
 *
3008
 *    Also, I gather that under some circumstances, a dblock
3009
 *    will be direct a flush dependency child both of the iblock
3010
 *    that points to it, and of the fractal heap header.
3011
 *
3012
 *    As a result of these changes, the functionality of these
3013
 *    sanity checking routines has been modified significantly.
3014
 *    Instead of scanning the fractal heap from a starting point
3015
 *    down, and verifying that there were no dirty entries, the
3016
 *    functions now scan downward from the starting point and
3017
 *    verify that there are no dirty flush dependency children
3018
 *    of the specified flush dependency parent.  In passing,
3019
 *    they also walk the data structure, and verify it.
3020
 *
3021
 * Return:  Non-negative on success/Negative on failure
3022
 *
3023
 *-------------------------------------------------------------------------
3024
 */
3025
#ifndef NDEBUG
3026
static herr_t
3027
H5HF__cache_verify_iblocks_dblocks_clean(H5F_t *f, haddr_t fd_parent_addr, H5HF_indirect_t *iblock,
3028
                                         bool *fd_clean, bool *clean, bool *has_dblocks)
3029
{
3030
    unsigned num_direct_rows;
3031
    unsigned max_dblock_index;
3032
    unsigned i;
3033
    haddr_t  iblock_addr;
3034
    herr_t   ret_value = SUCCEED; /* Return value */
3035
3036
    FUNC_ENTER_PACKAGE
3037
3038
    /* Sanity checks */
3039
    assert(f);
3040
    assert(H5_addr_defined(fd_parent_addr));
3041
    assert(iblock);
3042
    assert(iblock->cache_info.type == H5AC_FHEAP_IBLOCK);
3043
    assert(fd_clean);
3044
    assert(*fd_clean);
3045
    assert(clean); /* note that *clean need not be true */
3046
    assert(has_dblocks);
3047
3048
    i               = 0;
3049
    num_direct_rows = MIN(iblock->nrows, iblock->hdr->man_dtable.max_direct_rows);
3050
    assert(num_direct_rows <= iblock->nrows);
3051
    max_dblock_index = (num_direct_rows * iblock->hdr->man_dtable.cparam.width) - 1;
3052
    iblock_addr      = iblock->addr;
3053
    assert(H5_addr_defined(iblock_addr));
3054
3055
    while ((*fd_clean) && (i <= max_dblock_index)) {
3056
        haddr_t dblock_addr;
3057
3058
        dblock_addr = iblock->ents[i].addr;
3059
        if (H5_addr_defined(dblock_addr)) {
3060
            bool in_cache;
3061
            bool type_ok;
3062
3063
            if (H5AC_verify_entry_type(f, dblock_addr, &H5AC_FHEAP_DBLOCK[0], &in_cache, &type_ok) < 0)
3064
                HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't check dblock type");
3065
3066
            if (in_cache) { /* dblock is in cache */
3067
                bool     fd_exists;
3068
                unsigned dblock_status = 0;
3069
3070
                if (!type_ok)
3071
                    HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, "dblock addr doesn't refer to a dblock?!?");
3072
3073
                if (H5AC_get_entry_status(f, dblock_addr, &dblock_status) < 0)
3074
                    HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't get dblock status");
3075
3076
                assert(dblock_status & H5AC_ES__IN_CACHE);
3077
3078
                *has_dblocks = true;
3079
3080
                if ((dblock_status & H5AC_ES__IS_DIRTY) &&
3081
                    (((dblock_status & H5AC_ES__IMAGE_IS_UP_TO_DATE) == 0) ||
3082
                     (!H5AC_get_serialization_in_progress(f)))) {
3083
                    *clean = false;
3084
3085
                    if (H5AC_flush_dependency_exists(f, fd_parent_addr, dblock_addr, &fd_exists) < 0)
3086
                        HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't check flush dependency");
3087
3088
                    if (fd_exists)
3089
                        *fd_clean = false;
3090
                } /* end if */
3091
3092
                /* If a child dblock is in cache, it must have a flush
3093
                 * dependency relationship with this iblock.  Test this
3094
                 * here.
3095
                 */
3096
                if (H5AC_flush_dependency_exists(f, iblock_addr, dblock_addr, &fd_exists) < 0)
3097
                    HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't check flush dependency");
3098
3099
                if (!fd_exists)
3100
                    HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL,
3101
                                "dblock in cache and not a flush dep child of iblock.");
3102
            } /* end if */
3103
        }     /* end if */
3104
3105
        i++;
3106
    } /* end while */
3107
3108
done:
3109
    FUNC_LEAVE_NOAPI(ret_value)
3110
} /* H5HF__cache_verify_iblocks_dblocks_clean() */
3111
#endif /* NDEBUG */
3112
3113
/*------------------------------------------------------------------------
3114
 * Function:  H5HF__cache_verify_descendant_iblocks_clean
3115
 *
3116
 * Purpose: Sanity checking routine that attempts to verify that all
3117
 *    direct blocks pointed to by the supplied indirect block
3118
 *    are either clean, or not in the cache.
3119
 *
3120
 *    In passing, the function also does a cursory check to
3121
 *    spot any obvious errors in the flush dependency setup.
3122
 *    If any problems are found, the function returns failure.
3123
 *    Note that these checks are not exhaustive, thus passing
3124
 *    them does not mean that the flush dependencies are
3125
 *    correct -- only that there is nothing obviously wrong
3126
 *    with them.
3127
 *
3128
 *    WARNING:  This function presumes that the supplied
3129
 *    iblock is in the cache, and will not be removed
3130
 *    during the call.  Caller must ensure that this is
3131
 *    the case before the call.
3132
 *
3133
 *              Update -- 8/24/15
3134
 *
3135
 *              With the advent of the metadata cache image feature, it is
3136
 *              possible for the pre-serialize and serialize calls to be
3137
 *              invoked outside of a flush.  While this serialization
3138
 *              observes flush dependencies for the order of serialization,
3139
 *              the entries are not written to disk, and hence dirty entries
3140
 *              remain dirty.
3141
 *
3142
 *              To address this, updated the sanity checks in this function
3143
 *              to treat entries whose images are up to date as clean if
3144
 *              a cache serialization is in progress.
3145
 *
3146
 *    Update -- 9/29/16
3147
 *
3148
 *    The implementation of flush dependencies has been changed.
3149
 *    Prior to this change, a flush dependency parent could be
3150
 *    flushed if and only if all its flush dependency descendants
3151
 *    were clean.  In the new definition, a flush dependency
3152
 *    parent can be flushed if all its immediate flush dependency
3153
 *    children are clean, regardless of any other dirty
3154
 *    descendants.
3155
 *
3156
 *    Further, metadata cache entries are now allowed to have
3157
 *    multiple flush dependency parents.
3158
 *
3159
 *    This means that the fractal heap is no longer necessarily
3160
 *    flushed from the bottom up.
3161
 *
3162
 *    For example, it is now possible for a dirty fractal heap
3163
 *    header to be flushed before a dirty dblock, as long as the
3164
 *    there in an intervening iblock, and the header has no
3165
 *    dirty immediate flush dependency children.
3166
 *
3167
 *    Also, I gather that under some circumstances, a dblock
3168
 *    will be direct a flush dependency child both of the iblock
3169
 *    that points to it, and of the fractal heap header.
3170
 *
3171
 *    As a result of these changes, the functionality of these
3172
 *    sanity checking routines has been modified significantly.
3173
 *    Instead of scanning the fractal heap from a starting point
3174
 *    down, and verifying that there were no dirty entries, the
3175
 *    functions now scan downward from the starting point and
3176
 *    verify that there are no dirty flush dependency children
3177
 *    of the specified flush dependency parent.  In passing,
3178
 *    they also walk the data structure, and verify it.
3179
 *
3180
 *
3181
 * Return:  Non-negative on success/Negative on failure
3182
 *
3183
 *-------------------------------------------------------------------------
3184
 */
3185
#ifndef NDEBUG
3186
static herr_t
3187
H5HF__cache_verify_descendant_iblocks_clean(H5F_t *f, haddr_t fd_parent_addr, H5HF_indirect_t *iblock,
3188
                                            bool *fd_clean, bool *clean, bool *has_iblocks)
3189
{
3190
    unsigned first_iblock_index;
3191
    unsigned last_iblock_index;
3192
    unsigned num_direct_rows;
3193
    unsigned i;
3194
    haddr_t  iblock_addr;
3195
    herr_t   ret_value = SUCCEED; /* Return value */
3196
3197
    FUNC_ENTER_PACKAGE
3198
3199
    /* Sanity checks */
3200
    assert(f);
3201
    assert(H5_addr_defined(fd_parent_addr));
3202
    assert(iblock);
3203
    assert(iblock->cache_info.type == H5AC_FHEAP_IBLOCK);
3204
    assert(fd_clean);
3205
    assert(*fd_clean);
3206
    assert(clean); /* note that *clean need not be true */
3207
    assert(has_iblocks);
3208
    num_direct_rows = MIN(iblock->nrows, iblock->hdr->man_dtable.max_direct_rows);
3209
    assert(num_direct_rows <= iblock->nrows);
3210
3211
    iblock_addr        = iblock->addr;
3212
    first_iblock_index = num_direct_rows * iblock->hdr->man_dtable.cparam.width;
3213
    last_iblock_index  = (iblock->nrows * iblock->hdr->man_dtable.cparam.width) - 1;
3214
3215
    i = first_iblock_index;
3216
    while ((*fd_clean) && (i <= last_iblock_index)) {
3217
        haddr_t child_iblock_addr = iblock->ents[i].addr;
3218
3219
        if (H5_addr_defined(child_iblock_addr)) {
3220
            unsigned child_iblock_status = 0;
3221
3222
            if (H5AC_get_entry_status(f, child_iblock_addr, &child_iblock_status) < 0)
3223
                HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't get iblock status");
3224
3225
            if (child_iblock_status & H5AC_ES__IN_CACHE) {
3226
                bool fd_exists;
3227
3228
                *has_iblocks = true;
3229
3230
                if ((child_iblock_status & H5AC_ES__IS_DIRTY) &&
3231
                    (((child_iblock_status & H5AC_ES__IMAGE_IS_UP_TO_DATE) == 0) ||
3232
                     (!H5AC_get_serialization_in_progress(f)))) {
3233
3234
                    *clean = false;
3235
3236
                    if (H5AC_flush_dependency_exists(f, fd_parent_addr, child_iblock_addr, &fd_exists) < 0)
3237
                        HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't check flush dependency");
3238
3239
                    if (fd_exists)
3240
                        *fd_clean = false;
3241
                } /* end if */
3242
3243
                /* if the child iblock is in cache and *fd_clean is true,
3244
                 * we must continue to explore down the fractal heap tree
3245
                 * structure to verify that all descendant blocks that are
3246
                 * flush dependency children of the entry at parent_addr are
3247
                 * either clean, or not in the metadata cache.  We do this
3248
                 * with a recursive call to
3249
                 * H5HF__cache_verify_iblock_descendants_clean().
3250
                 * However, we can't make this call unless the child iblock
3251
                 * is somehow locked into the cache -- typically via either
3252
                 * pinning or protecting.
3253
                 *
3254
                 * If the child iblock is pinned, we can look up its pointer
3255
                 * on the current iblock's pinned child iblock list, and
3256
                 * and use that pointer in the recursive call.
3257
                 *
3258
                 * If the entry is unprotected and unpinned, we simply
3259
                 * protect it.
3260
                 *
3261
                 * If, however, the child iblock is already protected,
3262
                 * but not pinned, we have a bit of a problem, as we have
3263
                 * no legitimate way of looking up its pointer in memory.
3264
                 *
3265
                 * To solve this problem, I have added a new metadata cache
3266
                 * call to obtain the pointer.
3267
                 *
3268
                 * WARNING: This call should be used only in debugging
3269
                 *      routines, and it should be avoided there when
3270
                 *      possible.
3271
                 *
3272
                 *          Further, if we ever multi-thread the cache,
3273
                 *      this routine will have to be either discarded
3274
                 *      or heavily re-worked.
3275
                 *
3276
                 *      Finally, keep in mind that the entry whose
3277
                 *      pointer is obtained in this fashion may not
3278
                 *          be in a stable state.
3279
                 *
3280
                 * Assuming that the flush dependency code is working
3281
                 * as it should, the only reason for the child entry to
3282
                 * be unpinned is if none of its children are in cache.
3283
                 * This unfortunately means that if it is protected and
3284
                 * not pinned, the fractal heap is in the process of loading
3285
                 * or inserting one of its children.  The obvious implication
3286
                 * is that there is a significant chance that the child
3287
                 * iblock is in an unstable state.
3288
                 *
3289
                 * All this suggests that using the new call to obtain the
3290
                 * pointer to the protected child iblock is questionable
3291
                 * here.  However, since this is test/debugging code, I
3292
                 * expect that we will use this approach until it causes
3293
                 * problems, or we think of a better way.
3294
                 */
3295
                if (*fd_clean) {
3296
                    H5HF_indirect_t *child_iblock           = NULL;
3297
                    bool             unprotect_child_iblock = false;
3298
3299
                    if (0 == (child_iblock_status & H5AC_ES__IS_PINNED)) {
3300
                        /* child iblock is not pinned */
3301
                        if (0 == (child_iblock_status & H5AC_ES__IS_PROTECTED)) {
3302
                            /* child iblock is unprotected, and unpinned */
3303
                            /* protect it.  Note that the udata is only  */
3304
                            /* used in the load callback.  While the     */
3305
                            /* fractal heap makes heavy use of the udata */
3306
                            /* in this case, since we know that the      */
3307
                            /* entry is in cache, we can pass NULL udata */
3308
                            /*                                           */
3309
                            /* The tag associated specified in the API context  */
3310
                            /* we received as a parameter (via API context)  */
3311
                            /* may not be correct.                       */
3312
                            /*                                           */
3313
                            /* Grab the (hopefully) correct tag from the */
3314
                            /* parent iblock, and load it into the API context  */
3315
                            /* via the H5_BEGIN_TAG and H5_END_TAG       */
3316
                            /* macros.  Note that any error bracked by   */
3317
                            /* these macros must be reported with        */
3318
                            /* HGOTO_ERROR_TAG.                          */
3319
3320
                            H5_BEGIN_TAG(iblock->hdr->heap_addr)
3321
3322
                            if (NULL ==
3323
                                (child_iblock = (H5HF_indirect_t *)H5AC_protect(
3324
                                     f, H5AC_FHEAP_IBLOCK, child_iblock_addr, NULL, H5AC__READ_ONLY_FLAG)))
3325
                                HGOTO_ERROR_TAG(H5E_HEAP, H5E_CANTPROTECT, FAIL, "H5AC_protect() failed.");
3326
3327
                            H5_END_TAG
3328
3329
                            unprotect_child_iblock = true;
3330
                        } /* end if */
3331
                        else {
3332
                            /* child iblock is protected -- use             */
3333
                            /* H5AC_get_entry_ptr_from_addr() to get a      */
3334
                            /* pointer to the entry.  This is very slimy -- */
3335
                            /* come up with a better solution.              */
3336
                            if (H5AC_get_entry_ptr_from_addr(f, child_iblock_addr, (void **)(&child_iblock)) <
3337
                                0)
3338
                                HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL,
3339
                                            "H5AC_get_entry_ptr_from_addr() failed.");
3340
                            assert(child_iblock);
3341
                        } /* end else */
3342
                    }     /* end if */
3343
                    else {
3344
                        /* child iblock is pinned -- look it up in the */
3345
                        /* parent iblocks child_iblocks array.         */
3346
                        assert(iblock->child_iblocks);
3347
                        child_iblock = iblock->child_iblocks[i - first_iblock_index];
3348
                    } /* end else */
3349
3350
                    /* At this point, one way or another we should have
3351
                     * a pointer to the child iblock.  Verify that we
3352
                     * that we have the correct one.
3353
                     */
3354
                    assert(child_iblock);
3355
                    assert(child_iblock->cache_info.type == H5AC_FHEAP_IBLOCK);
3356
                    assert(child_iblock->addr == child_iblock_addr);
3357
3358
                    /* now make the recursive call */
3359
                    if (H5HF__cache_verify_iblock_descendants_clean(
3360
                            f, fd_parent_addr, child_iblock, &child_iblock_status, fd_clean, clean) < 0)
3361
                        HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, "can't verify child iblock clean.");
3362
3363
                    /* if iblock_addr != fd_parent_addr, verify that a flush
3364
                     * dependency relationship exists between iblock and
3365
                     * the child iblock.
3366
                     */
3367
                    if (fd_parent_addr != iblock_addr) {
3368
                        if (H5AC_flush_dependency_exists(f, iblock_addr, child_iblock_addr, &fd_exists) < 0)
3369
                            HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't check flush dependency");
3370
3371
                        if (!fd_exists)
3372
                            HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL,
3373
                                        "iblock is not a flush dep parent of child_iblock.");
3374
                    } /* end if */
3375
3376
                    /* if we protected the child iblock, unprotect it now */
3377
                    if (unprotect_child_iblock) {
3378
                        if (H5AC_unprotect(f, H5AC_FHEAP_IBLOCK, child_iblock_addr, child_iblock,
3379
                                           H5AC__NO_FLAGS_SET) < 0)
3380
                            HGOTO_ERROR(H5E_HEAP, H5E_CANTUNPROTECT, FAIL, "H5AC_unprotect() failed.");
3381
                    } /* end if */
3382
                }     /* end if */
3383
            }         /* end if */
3384
        }             /* end if */
3385
3386
        i++;
3387
    } /* end while */
3388
3389
done:
3390
    FUNC_LEAVE_NOAPI(ret_value)
3391
} /* H5HF__cache_verify_descendant_iblocks_clean() */
3392
#endif /* NDEBUG */