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