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