Line | Count | Source (jump to first uncovered line) |
1 | | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
2 | | * Copyright by The HDF Group. * |
3 | | * All rights reserved. * |
4 | | * * |
5 | | * This file is part of HDF5. The full HDF5 copyright notice, including * |
6 | | * terms governing use, modification, and redistribution, is contained in * |
7 | | * the LICENSE file, which can be found at the root of the source code * |
8 | | * distribution tree, or in https://www.hdfgroup.org/licenses. * |
9 | | * If you do not have access to either file, you may request a copy from * |
10 | | * help@hdfgroup.org. * |
11 | | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
12 | | |
13 | | /*------------------------------------------------------------------------- |
14 | | * |
15 | | * Created: H5PB.c |
16 | | * |
17 | | * Purpose: Page Buffer routines. |
18 | | * |
19 | | *------------------------------------------------------------------------- |
20 | | */ |
21 | | |
22 | | /****************/ |
23 | | /* Module Setup */ |
24 | | /****************/ |
25 | | |
26 | | #define H5F_FRIEND /* Suppress error about including H5Fpkg */ |
27 | | #include "H5PBmodule.h" /* This source code file is part of the H5PB module */ |
28 | | |
29 | | /***********/ |
30 | | /* Headers */ |
31 | | /***********/ |
32 | | #include "H5private.h" /* Generic Functions */ |
33 | | #include "H5Eprivate.h" /* Error handling */ |
34 | | #include "H5Fpkg.h" /* Files */ |
35 | | #include "H5FDprivate.h" /* File drivers */ |
36 | | #include "H5FLprivate.h" /* Free Lists */ |
37 | | #include "H5MMprivate.h" /* Memory management */ |
38 | | #include "H5PBpkg.h" /* File access */ |
39 | | #include "H5SLprivate.h" /* Skip List */ |
40 | | |
41 | | /****************/ |
42 | | /* Local Macros */ |
43 | | /****************/ |
44 | | #define H5PB__PREPEND(page_ptr, head_ptr, tail_ptr, len) \ |
45 | 0 | { \ |
46 | 0 | if ((head_ptr) == NULL) { \ |
47 | 0 | (head_ptr) = (page_ptr); \ |
48 | 0 | (tail_ptr) = (page_ptr); \ |
49 | 0 | } /* end if */ \ |
50 | 0 | else { \ |
51 | 0 | (head_ptr)->prev = (page_ptr); \ |
52 | 0 | (page_ptr)->next = (head_ptr); \ |
53 | 0 | (head_ptr) = (page_ptr); \ |
54 | 0 | } /* end else */ \ |
55 | 0 | (len)++; \ |
56 | 0 | } /* H5PB__PREPEND() */ |
57 | | |
58 | | #define H5PB__REMOVE(page_ptr, head_ptr, tail_ptr, len) \ |
59 | 0 | { \ |
60 | 0 | if ((head_ptr) == (page_ptr)) { \ |
61 | 0 | (head_ptr) = (page_ptr)->next; \ |
62 | 0 | if ((head_ptr) != NULL) \ |
63 | 0 | (head_ptr)->prev = NULL; \ |
64 | 0 | } /* end if */ \ |
65 | 0 | else \ |
66 | 0 | (page_ptr)->prev->next = (page_ptr)->next; \ |
67 | 0 | if ((tail_ptr) == (page_ptr)) { \ |
68 | 0 | (tail_ptr) = (page_ptr)->prev; \ |
69 | 0 | if ((tail_ptr) != NULL) \ |
70 | 0 | (tail_ptr)->next = NULL; \ |
71 | 0 | } /* end if */ \ |
72 | 0 | else \ |
73 | 0 | (page_ptr)->next->prev = (page_ptr)->prev; \ |
74 | 0 | page_ptr->next = NULL; \ |
75 | 0 | page_ptr->prev = NULL; \ |
76 | 0 | (len)--; \ |
77 | 0 | } |
78 | | |
79 | | #define H5PB__INSERT_LRU(page_buf, page_ptr) \ |
80 | 0 | { \ |
81 | 0 | assert(page_buf); \ |
82 | 0 | assert(page_ptr); \ |
83 | 0 | /* insert the entry at the head of the list. */ \ |
84 | 0 | H5PB__PREPEND((page_ptr), (page_buf)->LRU_head_ptr, (page_buf)->LRU_tail_ptr, \ |
85 | 0 | (page_buf)->LRU_list_len) \ |
86 | 0 | } |
87 | | |
88 | | #define H5PB__REMOVE_LRU(page_buf, page_ptr) \ |
89 | 0 | { \ |
90 | 0 | assert(page_buf); \ |
91 | 0 | assert(page_ptr); \ |
92 | 0 | /* remove the entry from the list. */ \ |
93 | 0 | H5PB__REMOVE((page_ptr), (page_buf)->LRU_head_ptr, (page_buf)->LRU_tail_ptr, \ |
94 | 0 | (page_buf)->LRU_list_len) \ |
95 | 0 | } |
96 | | |
97 | | #define H5PB__MOVE_TO_TOP_LRU(page_buf, page_ptr) \ |
98 | 0 | { \ |
99 | 0 | assert(page_buf); \ |
100 | 0 | assert(page_ptr); \ |
101 | 0 | /* Remove entry and insert at the head of the list. */ \ |
102 | 0 | H5PB__REMOVE((page_ptr), (page_buf)->LRU_head_ptr, (page_buf)->LRU_tail_ptr, \ |
103 | 0 | (page_buf)->LRU_list_len) \ |
104 | 0 | H5PB__PREPEND((page_ptr), (page_buf)->LRU_head_ptr, (page_buf)->LRU_tail_ptr, \ |
105 | 0 | (page_buf)->LRU_list_len) \ |
106 | 0 | } |
107 | | |
108 | | /******************/ |
109 | | /* Local Typedefs */ |
110 | | /******************/ |
111 | | |
112 | | /* Iteration context for destroying page buffer */ |
113 | | typedef struct { |
114 | | H5PB_t *page_buf; |
115 | | bool actual_slist; |
116 | | } H5PB_ud1_t; |
117 | | |
118 | | /********************/ |
119 | | /* Package Typedefs */ |
120 | | /********************/ |
121 | | |
122 | | /********************/ |
123 | | /* Local Prototypes */ |
124 | | /********************/ |
125 | | static herr_t H5PB__insert_entry(H5PB_t *page_buf, H5PB_entry_t *page_entry); |
126 | | static htri_t H5PB__make_space(H5F_shared_t *f_sh, H5PB_t *page_buf, H5FD_mem_t inserted_type); |
127 | | static herr_t H5PB__write_entry(H5F_shared_t *f_sh, H5PB_entry_t *page_entry); |
128 | | |
129 | | /*********************/ |
130 | | /* Package Variables */ |
131 | | /*********************/ |
132 | | |
133 | | /* Package initialization variable */ |
134 | | bool H5_PKG_INIT_VAR = false; |
135 | | |
136 | | /*****************************/ |
137 | | /* Library Private Variables */ |
138 | | /*****************************/ |
139 | | |
140 | | /*******************/ |
141 | | /* Local Variables */ |
142 | | /*******************/ |
143 | | /* Declare a free list to manage the H5PB_t struct */ |
144 | | H5FL_DEFINE_STATIC(H5PB_t); |
145 | | |
146 | | /* Declare a free list to manage the H5PB_entry_t struct */ |
147 | | H5FL_DEFINE_STATIC(H5PB_entry_t); |
148 | | |
149 | | /*------------------------------------------------------------------------- |
150 | | * Function: H5PB_reset_stats |
151 | | * |
152 | | * Purpose: This function was created without documentation. |
153 | | * What follows is my best understanding of Mohamad's intent. |
154 | | * |
155 | | * Reset statistics collected for the page buffer layer. |
156 | | * |
157 | | * Return: Non-negative on success/Negative on failure |
158 | | * |
159 | | *------------------------------------------------------------------------- |
160 | | */ |
161 | | herr_t |
162 | | H5PB_reset_stats(H5PB_t *page_buf) |
163 | 0 | { |
164 | 0 | FUNC_ENTER_NOAPI_NOERR |
165 | | |
166 | | /* Sanity checks */ |
167 | 0 | assert(page_buf); |
168 | |
|
169 | 0 | page_buf->accesses[0] = 0; |
170 | 0 | page_buf->accesses[1] = 0; |
171 | 0 | page_buf->hits[0] = 0; |
172 | 0 | page_buf->hits[1] = 0; |
173 | 0 | page_buf->misses[0] = 0; |
174 | 0 | page_buf->misses[1] = 0; |
175 | 0 | page_buf->evictions[0] = 0; |
176 | 0 | page_buf->evictions[1] = 0; |
177 | 0 | page_buf->bypasses[0] = 0; |
178 | 0 | page_buf->bypasses[1] = 0; |
179 | |
|
180 | 0 | FUNC_LEAVE_NOAPI(SUCCEED) |
181 | 0 | } /* H5PB_reset_stats() */ |
182 | | |
183 | | /*------------------------------------------------------------------------- |
184 | | * Function: H5PB_get_stats |
185 | | * |
186 | | * Purpose: This function was created without documentation. |
187 | | * What follows is my best understanding of Mohamad's intent. |
188 | | * |
189 | | * Retrieve statistics collected about page accesses for the page buffer layer. |
190 | | * --accesses: the number of metadata and raw data accesses to the page buffer layer |
191 | | * --hits: the number of metadata and raw data hits in the page buffer layer |
192 | | * --misses: the number of metadata and raw data misses in the page buffer layer |
193 | | * --evictions: the number of metadata and raw data evictions from the page buffer layer |
194 | | * --bypasses: the number of metadata and raw data accesses that bypass the page buffer layer |
195 | | * |
196 | | * Return: Non-negative on success/Negative on failure |
197 | | * |
198 | | *------------------------------------------------------------------------- |
199 | | */ |
200 | | herr_t |
201 | | H5PB_get_stats(const H5PB_t *page_buf, unsigned accesses[2], unsigned hits[2], unsigned misses[2], |
202 | | unsigned evictions[2], unsigned bypasses[2]) |
203 | 0 | { |
204 | 0 | FUNC_ENTER_NOAPI_NOERR |
205 | | |
206 | | /* Sanity checks */ |
207 | 0 | assert(page_buf); |
208 | |
|
209 | 0 | accesses[0] = page_buf->accesses[0]; |
210 | 0 | accesses[1] = page_buf->accesses[1]; |
211 | 0 | hits[0] = page_buf->hits[0]; |
212 | 0 | hits[1] = page_buf->hits[1]; |
213 | 0 | misses[0] = page_buf->misses[0]; |
214 | 0 | misses[1] = page_buf->misses[1]; |
215 | 0 | evictions[0] = page_buf->evictions[0]; |
216 | 0 | evictions[1] = page_buf->evictions[1]; |
217 | 0 | bypasses[0] = page_buf->bypasses[0]; |
218 | 0 | bypasses[1] = page_buf->bypasses[1]; |
219 | |
|
220 | 0 | FUNC_LEAVE_NOAPI(SUCCEED) |
221 | 0 | } /* H5PB_get_stats */ |
222 | | |
223 | | /*------------------------------------------------------------------------- |
224 | | * Function: H5PB_print_stats() |
225 | | * |
226 | | * Purpose: This function was created without documentation. |
227 | | * What follows is my best understanding of Mohamad's intent. |
228 | | * |
229 | | * Print out statistics collected for the page buffer layer. |
230 | | * |
231 | | * Return: Non-negative on success/Negative on failure |
232 | | * |
233 | | *------------------------------------------------------------------------- |
234 | | */ |
235 | | herr_t |
236 | | H5PB_print_stats(const H5PB_t *page_buf) |
237 | 0 | { |
238 | 0 | FUNC_ENTER_NOAPI_NOINIT_NOERR |
239 | |
|
240 | 0 | assert(page_buf); |
241 | |
|
242 | 0 | printf("PAGE BUFFER STATISTICS:\n"); |
243 | |
|
244 | 0 | printf("******* METADATA\n"); |
245 | 0 | printf("\t Total Accesses: %u\n", page_buf->accesses[0]); |
246 | 0 | printf("\t Hits: %u\n", page_buf->hits[0]); |
247 | 0 | printf("\t Misses: %u\n", page_buf->misses[0]); |
248 | 0 | printf("\t Evictions: %u\n", page_buf->evictions[0]); |
249 | 0 | printf("\t Bypasses: %u\n", page_buf->bypasses[0]); |
250 | 0 | printf("\t Hit Rate = %f%%\n", |
251 | 0 | ((double)page_buf->hits[0] / (page_buf->accesses[0] - page_buf->bypasses[0])) * 100); |
252 | 0 | printf("*****************\n\n"); |
253 | |
|
254 | 0 | printf("******* RAWDATA\n"); |
255 | 0 | printf("\t Total Accesses: %u\n", page_buf->accesses[1]); |
256 | 0 | printf("\t Hits: %u\n", page_buf->hits[1]); |
257 | 0 | printf("\t Misses: %u\n", page_buf->misses[1]); |
258 | 0 | printf("\t Evictions: %u\n", page_buf->evictions[1]); |
259 | 0 | printf("\t Bypasses: %u\n", page_buf->bypasses[1]); |
260 | 0 | printf("\t Hit Rate = %f%%\n", |
261 | 0 | ((double)page_buf->hits[1] / (page_buf->accesses[1] - page_buf->bypasses[0])) * 100); |
262 | 0 | printf("*****************\n\n"); |
263 | |
|
264 | 0 | FUNC_LEAVE_NOAPI(SUCCEED) |
265 | 0 | } /* H5PB_print_stats */ |
266 | | |
267 | | /*------------------------------------------------------------------------- |
268 | | * Function: H5PB_create |
269 | | * |
270 | | * Purpose: Create and setup the PB on the file. |
271 | | * |
272 | | * Return: Non-negative on success/Negative on failure |
273 | | * |
274 | | *------------------------------------------------------------------------- |
275 | | */ |
276 | | herr_t |
277 | | H5PB_create(H5F_shared_t *f_sh, size_t size, unsigned page_buf_min_meta_perc, unsigned page_buf_min_raw_perc) |
278 | 0 | { |
279 | 0 | H5PB_t *page_buf = NULL; |
280 | 0 | herr_t ret_value = SUCCEED; /* Return value */ |
281 | |
|
282 | 0 | FUNC_ENTER_NOAPI(FAIL) |
283 | | |
284 | | /* Sanity checks */ |
285 | 0 | assert(f_sh); |
286 | | |
287 | | /* Check args */ |
288 | 0 | if (f_sh->fs_strategy != H5F_FSPACE_STRATEGY_PAGE) |
289 | 0 | HGOTO_ERROR(H5E_FILE, H5E_CANTINIT, FAIL, |
290 | 0 | "Enabling Page Buffering requires PAGE file space strategy"); |
291 | | /* round down the size if it is larger than the page size */ |
292 | 0 | else if (size > f_sh->fs_page_size) { |
293 | 0 | hsize_t temp_size; |
294 | |
|
295 | 0 | temp_size = (size / f_sh->fs_page_size) * f_sh->fs_page_size; |
296 | 0 | H5_CHECKED_ASSIGN(size, size_t, temp_size, hsize_t); |
297 | 0 | } /* end if */ |
298 | 0 | else if (0 != size % f_sh->fs_page_size) |
299 | 0 | HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTINIT, FAIL, "Page Buffer size must be >= to the page size"); |
300 | | |
301 | | /* Allocate the new page buffering structure */ |
302 | 0 | if (NULL == (page_buf = H5FL_CALLOC(H5PB_t))) |
303 | 0 | HGOTO_ERROR(H5E_PAGEBUF, H5E_NOSPACE, FAIL, "memory allocation failed"); |
304 | | |
305 | 0 | page_buf->max_size = size; |
306 | 0 | H5_CHECKED_ASSIGN(page_buf->page_size, size_t, f_sh->fs_page_size, hsize_t); |
307 | 0 | page_buf->min_meta_perc = page_buf_min_meta_perc; |
308 | 0 | page_buf->min_raw_perc = page_buf_min_raw_perc; |
309 | | |
310 | | /* Calculate the minimum page count for metadata and raw data |
311 | | * based on the fractions provided |
312 | | */ |
313 | 0 | page_buf->min_meta_count = (unsigned)((size * page_buf_min_meta_perc) / (f_sh->fs_page_size * 100)); |
314 | 0 | page_buf->min_raw_count = (unsigned)((size * page_buf_min_raw_perc) / (f_sh->fs_page_size * 100)); |
315 | |
|
316 | 0 | if (NULL == (page_buf->slist_ptr = H5SL_create(H5SL_TYPE_HADDR, NULL))) |
317 | 0 | HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTCREATE, FAIL, "can't create skip list"); |
318 | 0 | if (NULL == (page_buf->mf_slist_ptr = H5SL_create(H5SL_TYPE_HADDR, NULL))) |
319 | 0 | HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTCREATE, FAIL, "can't create skip list"); |
320 | | |
321 | 0 | if (NULL == (page_buf->page_fac = H5FL_fac_init(page_buf->page_size))) |
322 | 0 | HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTINIT, FAIL, "can't create page factory"); |
323 | | |
324 | 0 | f_sh->page_buf = page_buf; |
325 | |
|
326 | 0 | done: |
327 | 0 | if (ret_value < 0) { |
328 | 0 | if (page_buf != NULL) { |
329 | 0 | if (page_buf->slist_ptr != NULL) |
330 | 0 | H5SL_close(page_buf->slist_ptr); |
331 | 0 | if (page_buf->mf_slist_ptr != NULL) |
332 | 0 | H5SL_close(page_buf->mf_slist_ptr); |
333 | 0 | if (page_buf->page_fac != NULL) |
334 | 0 | H5FL_fac_term(page_buf->page_fac); |
335 | 0 | page_buf = H5FL_FREE(H5PB_t, page_buf); |
336 | 0 | } /* end if */ |
337 | 0 | } /* end if */ |
338 | |
|
339 | 0 | FUNC_LEAVE_NOAPI(ret_value) |
340 | 0 | } /* H5PB_create */ |
341 | | |
342 | | /*------------------------------------------------------------------------- |
343 | | * Function: H5PB__flush_cb |
344 | | * |
345 | | * Purpose: Callback to flush PB skiplist entries. |
346 | | * |
347 | | * Return: Non-negative on success/Negative on failure |
348 | | * |
349 | | *------------------------------------------------------------------------- |
350 | | */ |
351 | | static herr_t |
352 | | H5PB__flush_cb(void *item, void H5_ATTR_UNUSED *key, void *_op_data) |
353 | 0 | { |
354 | 0 | H5PB_entry_t *page_entry = (H5PB_entry_t *)item; /* Pointer to page entry node */ |
355 | 0 | H5F_shared_t *f_sh = (H5F_shared_t *)_op_data; |
356 | 0 | herr_t ret_value = SUCCEED; /* Return value */ |
357 | |
|
358 | 0 | FUNC_ENTER_PACKAGE |
359 | | |
360 | | /* Sanity checks */ |
361 | 0 | assert(page_entry); |
362 | 0 | assert(f_sh); |
363 | | |
364 | | /* Flush the page if it's dirty */ |
365 | 0 | if (page_entry->is_dirty) |
366 | 0 | if (H5PB__write_entry(f_sh, page_entry) < 0) |
367 | 0 | HGOTO_ERROR(H5E_PAGEBUF, H5E_WRITEERROR, FAIL, "file write failed"); |
368 | | |
369 | 0 | done: |
370 | 0 | FUNC_LEAVE_NOAPI(ret_value) |
371 | 0 | } /* H5PB__flush_cb() */ |
372 | | |
373 | | /*------------------------------------------------------------------------- |
374 | | * Function: H5PB_flush |
375 | | * |
376 | | * Purpose: Flush/Free all the PB entries to the file. |
377 | | * |
378 | | * Return: Non-negative on success/Negative on failure |
379 | | * |
380 | | *------------------------------------------------------------------------- |
381 | | */ |
382 | | herr_t |
383 | | H5PB_flush(H5F_shared_t *f_sh) |
384 | 1 | { |
385 | 1 | herr_t ret_value = SUCCEED; /* Return value */ |
386 | | |
387 | 1 | FUNC_ENTER_NOAPI(FAIL) |
388 | | |
389 | | /* Sanity check */ |
390 | 1 | assert(f_sh); |
391 | | |
392 | | /* Flush all the entries in the PB skiplist, if we have write access on the file */ |
393 | 1 | if (f_sh->page_buf && (H5F_ACC_RDWR & H5F_SHARED_INTENT(f_sh))) { |
394 | 0 | H5PB_t *page_buf = f_sh->page_buf; |
395 | | |
396 | | /* Iterate over all entries in page buffer skip list */ |
397 | 0 | if (H5SL_iterate(page_buf->slist_ptr, H5PB__flush_cb, f_sh)) |
398 | 0 | HGOTO_ERROR(H5E_PAGEBUF, H5E_BADITER, FAIL, "can't flush page buffer skip list"); |
399 | 0 | } /* end if */ |
400 | | |
401 | 1 | done: |
402 | 1 | FUNC_LEAVE_NOAPI(ret_value) |
403 | 1 | } /* H5PB_flush */ |
404 | | |
405 | | /*------------------------------------------------------------------------- |
406 | | * Function: H5PB__dest_cb |
407 | | * |
408 | | * Purpose: Callback to free PB skiplist entries. |
409 | | * |
410 | | * Return: Non-negative on success/Negative on failure |
411 | | * |
412 | | *------------------------------------------------------------------------- |
413 | | */ |
414 | | static herr_t |
415 | | H5PB__dest_cb(void *item, void H5_ATTR_UNUSED *key, void *_op_data) |
416 | 0 | { |
417 | 0 | H5PB_entry_t *page_entry = (H5PB_entry_t *)item; /* Pointer to page entry node */ |
418 | 0 | H5PB_ud1_t *op_data = (H5PB_ud1_t *)_op_data; |
419 | |
|
420 | 0 | FUNC_ENTER_PACKAGE_NOERR |
421 | | |
422 | | /* Sanity checking */ |
423 | 0 | assert(page_entry); |
424 | 0 | assert(op_data); |
425 | 0 | assert(op_data->page_buf); |
426 | | |
427 | | /* Remove entry from LRU list */ |
428 | 0 | if (op_data->actual_slist) { |
429 | 0 | H5PB__REMOVE_LRU(op_data->page_buf, page_entry) |
430 | 0 | page_entry->page_buf_ptr = H5FL_FAC_FREE(op_data->page_buf->page_fac, page_entry->page_buf_ptr); |
431 | 0 | } /* end if */ |
432 | | |
433 | | /* Free page entry */ |
434 | 0 | page_entry = H5FL_FREE(H5PB_entry_t, page_entry); |
435 | |
|
436 | 0 | FUNC_LEAVE_NOAPI(SUCCEED) |
437 | 0 | } /* H5PB__dest_cb() */ |
438 | | |
439 | | /*------------------------------------------------------------------------- |
440 | | * Function: H5PB_dest |
441 | | * |
442 | | * Purpose: Flush and destroy the PB on the file if it exists. |
443 | | * |
444 | | * Return: Non-negative on success/Negative on failure |
445 | | * |
446 | | *------------------------------------------------------------------------- |
447 | | */ |
448 | | herr_t |
449 | | H5PB_dest(H5F_shared_t *f_sh) |
450 | 7 | { |
451 | 7 | herr_t ret_value = SUCCEED; /* Return value */ |
452 | | |
453 | 7 | FUNC_ENTER_NOAPI(FAIL) |
454 | | |
455 | | /* Sanity checks */ |
456 | 7 | assert(f_sh); |
457 | | |
458 | | /* flush and destroy the page buffer, if it exists */ |
459 | 7 | if (f_sh->page_buf) { |
460 | 0 | H5PB_t *page_buf = f_sh->page_buf; |
461 | 0 | H5PB_ud1_t op_data; /* Iteration context */ |
462 | |
|
463 | 0 | if (H5PB_flush(f_sh) < 0) |
464 | 0 | HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTFLUSH, FAIL, "can't flush page buffer"); |
465 | | |
466 | | /* Set up context info */ |
467 | 0 | op_data.page_buf = page_buf; |
468 | | |
469 | | /* Destroy the skip list containing all the entries in the PB */ |
470 | 0 | op_data.actual_slist = true; |
471 | 0 | if (H5SL_destroy(page_buf->slist_ptr, H5PB__dest_cb, &op_data)) |
472 | 0 | HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTCLOSEOBJ, FAIL, "can't destroy page buffer skip list"); |
473 | | |
474 | | /* Destroy the skip list containing the new entries */ |
475 | 0 | op_data.actual_slist = false; |
476 | 0 | if (H5SL_destroy(page_buf->mf_slist_ptr, H5PB__dest_cb, &op_data)) |
477 | 0 | HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTCLOSEOBJ, FAIL, "can't destroy page buffer skip list"); |
478 | | |
479 | | /* Destroy the page factory */ |
480 | 0 | if (H5FL_fac_term(page_buf->page_fac) < 0) |
481 | 0 | HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTRELEASE, FAIL, "can't destroy page buffer page factory"); |
482 | | |
483 | 0 | f_sh->page_buf = H5FL_FREE(H5PB_t, page_buf); |
484 | 0 | } /* end if */ |
485 | | |
486 | 7 | done: |
487 | 7 | FUNC_LEAVE_NOAPI(ret_value) |
488 | 7 | } /* H5PB_dest */ |
489 | | |
490 | | /*------------------------------------------------------------------------- |
491 | | * Function: H5PB_add_new_page |
492 | | * |
493 | | * Purpose: Add a new page to the new page skip list. This is called |
494 | | * from the MF layer when a new page is allocated to |
495 | | * indicate to the page buffer layer that a read of the page |
496 | | * from the file is not necessary since it's an empty page. |
497 | | * |
498 | | * Return: Non-negative on success/Negative on failure |
499 | | * |
500 | | *------------------------------------------------------------------------- |
501 | | */ |
502 | | herr_t |
503 | | H5PB_add_new_page(H5F_shared_t *f_sh, H5FD_mem_t type, haddr_t page_addr) |
504 | 0 | { |
505 | 0 | H5PB_t *page_buf; /* Page buffer to operate on */ |
506 | 0 | H5PB_entry_t *page_entry = NULL; /* Pointer to the corresponding page entry */ |
507 | 0 | herr_t ret_value = SUCCEED; /* Return value */ |
508 | |
|
509 | 0 | FUNC_ENTER_NOAPI(FAIL) |
510 | | |
511 | | /* Sanity checks */ |
512 | 0 | assert(f_sh); |
513 | 0 | page_buf = f_sh->page_buf; |
514 | 0 | assert(page_buf); |
515 | | |
516 | | /* If there is an existing page, this means that at some point the |
517 | | * file free space manager freed and re-allocated a page at the same |
518 | | * address. No need to do anything here then... |
519 | | */ |
520 | | /* MSC - to be safe, might want to dig in the MF layer and remove |
521 | | * the page when it is freed from this list if it still exists and |
522 | | * remove this check |
523 | | */ |
524 | 0 | if (NULL == H5SL_search(page_buf->mf_slist_ptr, &(page_addr))) { |
525 | | /* Create the new PB entry */ |
526 | 0 | if (NULL == (page_entry = H5FL_CALLOC(H5PB_entry_t))) |
527 | 0 | HGOTO_ERROR(H5E_PAGEBUF, H5E_NOSPACE, FAIL, "memory allocation failed"); |
528 | | |
529 | | /* Initialize page fields */ |
530 | 0 | page_entry->addr = page_addr; |
531 | 0 | page_entry->type = (H5F_mem_page_t)type; |
532 | 0 | page_entry->is_dirty = false; |
533 | | |
534 | | /* Insert entry in skip list */ |
535 | 0 | if (H5SL_insert(page_buf->mf_slist_ptr, page_entry, &(page_entry->addr)) < 0) |
536 | 0 | HGOTO_ERROR(H5E_PAGEBUF, H5E_BADVALUE, FAIL, "Can't insert entry in skip list"); |
537 | 0 | } /* end if */ |
538 | | |
539 | 0 | done: |
540 | 0 | if (ret_value < 0) |
541 | 0 | if (page_entry) |
542 | 0 | page_entry = H5FL_FREE(H5PB_entry_t, page_entry); |
543 | |
|
544 | 0 | FUNC_LEAVE_NOAPI(ret_value) |
545 | 0 | } /* H5PB_add_new_page */ |
546 | | |
547 | | /*------------------------------------------------------------------------- |
548 | | * Function: H5PB_update_entry |
549 | | * |
550 | | * Purpose: In PHDF5, entries that are written by other processes and just |
551 | | * marked clean by this process have to have their corresponding |
552 | | * pages updated if they exist in the page buffer. |
553 | | * This routine checks and update the pages. |
554 | | * |
555 | | * Return: Non-negative on success/Negative on failure |
556 | | * |
557 | | *------------------------------------------------------------------------- |
558 | | */ |
559 | | herr_t |
560 | | H5PB_update_entry(H5PB_t *page_buf, haddr_t addr, size_t size, const void *buf) |
561 | 0 | { |
562 | 0 | H5PB_entry_t *page_entry; /* Pointer to the corresponding page entry */ |
563 | 0 | haddr_t page_addr; |
564 | |
|
565 | 0 | FUNC_ENTER_NOAPI_NOERR |
566 | | |
567 | | /* Sanity checks */ |
568 | 0 | assert(page_buf); |
569 | 0 | assert(size <= page_buf->page_size); |
570 | 0 | assert(buf); |
571 | | |
572 | | /* calculate the aligned address of the first page */ |
573 | 0 | page_addr = (addr / page_buf->page_size) * page_buf->page_size; |
574 | | |
575 | | /* search for the page and update if found */ |
576 | 0 | page_entry = (H5PB_entry_t *)H5SL_search(page_buf->slist_ptr, (void *)(&page_addr)); |
577 | 0 | if (page_entry) { |
578 | 0 | haddr_t offset; |
579 | |
|
580 | 0 | assert(addr + size <= page_addr + page_buf->page_size); |
581 | 0 | offset = addr - page_addr; |
582 | 0 | H5MM_memcpy((uint8_t *)page_entry->page_buf_ptr + offset, buf, size); |
583 | | |
584 | | /* move to top of LRU list */ |
585 | 0 | H5PB__MOVE_TO_TOP_LRU(page_buf, page_entry) |
586 | 0 | } /* end if */ |
587 | |
|
588 | 0 | FUNC_LEAVE_NOAPI(SUCCEED) |
589 | 0 | } /* H5PB_update_entry */ |
590 | | |
591 | | /*------------------------------------------------------------------------- |
592 | | * Function: H5PB_remove_entry |
593 | | * |
594 | | * Purpose: Remove possible metadata entry with ADDR from the PB cache. |
595 | | * This is in response to the data corruption bug from fheap.c |
596 | | * with page buffering + page strategy. |
597 | | * Note: Large metadata page bypasses the PB cache. |
598 | | * Note: Update of raw data page (large or small sized) is handled by the PB cache. |
599 | | * |
600 | | * Return: Non-negative on success/Negative on failure |
601 | | * |
602 | | *------------------------------------------------------------------------- |
603 | | */ |
604 | | herr_t |
605 | | H5PB_remove_entry(const H5F_shared_t *f_sh, haddr_t addr) |
606 | 0 | { |
607 | 0 | H5PB_t *page_buf; /* Page buffer to operate on */ |
608 | 0 | H5PB_entry_t *page_entry = NULL; /* Pointer to the page entry being searched */ |
609 | 0 | herr_t ret_value = SUCCEED; /* Return value */ |
610 | |
|
611 | 0 | FUNC_ENTER_NOAPI(FAIL) |
612 | | |
613 | | /* Sanity checks */ |
614 | 0 | assert(f_sh); |
615 | 0 | page_buf = f_sh->page_buf; |
616 | 0 | assert(page_buf); |
617 | | |
618 | | /* Search for address in the skip list */ |
619 | 0 | page_entry = (H5PB_entry_t *)H5SL_search(page_buf->slist_ptr, (void *)(&addr)); |
620 | | |
621 | | /* If found, remove the entry from the PB cache */ |
622 | 0 | if (page_entry) { |
623 | 0 | assert(page_entry->type != H5F_MEM_PAGE_DRAW); |
624 | 0 | if (NULL == H5SL_remove(page_buf->slist_ptr, &(page_entry->addr))) |
625 | 0 | HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "Page Entry is not in skip list"); |
626 | | |
627 | | /* Remove from LRU list */ |
628 | 0 | H5PB__REMOVE_LRU(page_buf, page_entry) |
629 | 0 | assert(H5SL_count(page_buf->slist_ptr) == page_buf->LRU_list_len); |
630 | |
|
631 | 0 | page_buf->meta_count--; |
632 | |
|
633 | 0 | page_entry->page_buf_ptr = H5FL_FAC_FREE(page_buf->page_fac, page_entry->page_buf_ptr); |
634 | 0 | page_entry = H5FL_FREE(H5PB_entry_t, page_entry); |
635 | 0 | } /* end if */ |
636 | | |
637 | 0 | done: |
638 | 0 | FUNC_LEAVE_NOAPI(ret_value) |
639 | 0 | } /* H5PB_remove_entry */ |
640 | | |
641 | | /*------------------------------------------------------------------------- |
642 | | * Function: H5PB_read |
643 | | * |
644 | | * Purpose: Reads in the data from the page containing it if it exists |
645 | | * in the PB cache; otherwise reads in the page through the VFD. |
646 | | * |
647 | | * Return: Non-negative on success/Negative on failure |
648 | | * |
649 | | *------------------------------------------------------------------------- |
650 | | */ |
651 | | herr_t |
652 | | H5PB_read(H5F_shared_t *f_sh, H5FD_mem_t type, haddr_t addr, size_t size, void *buf /*out*/) |
653 | 9 | { |
654 | 9 | H5PB_t *page_buf; /* Page buffering info for this file */ |
655 | 9 | H5PB_entry_t *page_entry; /* Pointer to the corresponding page entry */ |
656 | 9 | H5FD_t *file; /* File driver pointer */ |
657 | 9 | haddr_t first_page_addr, last_page_addr; /* Addresses of the first and last pages covered by I/O */ |
658 | 9 | haddr_t offset; |
659 | 9 | haddr_t search_addr; /* Address of current page */ |
660 | 9 | hsize_t num_touched_pages; /* Number of pages accessed */ |
661 | 9 | size_t access_size = 0; |
662 | 9 | bool bypass_pb = false; /* Whether to bypass page buffering */ |
663 | 9 | hsize_t i; /* Local index variable */ |
664 | 9 | herr_t ret_value = SUCCEED; /* Return value */ |
665 | | |
666 | 9 | FUNC_ENTER_NOAPI(FAIL) |
667 | | |
668 | | /* Sanity checks */ |
669 | 9 | assert(f_sh); |
670 | 9 | assert(type != H5FD_MEM_GHEAP); |
671 | | |
672 | | /* Get pointer to page buffer info for this file */ |
673 | 9 | page_buf = f_sh->page_buf; |
674 | | |
675 | | #ifdef H5_HAVE_PARALLEL |
676 | | if (H5F_SHARED_HAS_FEATURE(f_sh, H5FD_FEAT_HAS_MPI)) { |
677 | | #if 1 |
678 | | bypass_pb = true; |
679 | | #else |
680 | | /* MSC - why this stopped working ? */ |
681 | | int mpi_size; |
682 | | |
683 | | if ((mpi_size = H5F_shared_mpi_get_size(f_sh)) < 0) |
684 | | HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTGET, FAIL, "can't retrieve MPI communicator size"); |
685 | | if (1 != mpi_size) |
686 | | bypass_pb = true; |
687 | | #endif |
688 | | } /* end if */ |
689 | | #endif |
690 | | |
691 | | /* If page buffering is disabled, or the I/O size is larger than that of a |
692 | | * single page, or if this is a parallel raw data access, bypass page |
693 | | * buffering. |
694 | | */ |
695 | 9 | if (NULL == page_buf || size >= page_buf->page_size || (bypass_pb && H5FD_MEM_DRAW == type)) { |
696 | 9 | if (H5F__accum_read(f_sh, type, addr, size, buf) < 0) |
697 | 0 | HGOTO_ERROR(H5E_PAGEBUF, H5E_READERROR, FAIL, "read through metadata accumulator failed"); |
698 | | |
699 | | /* Update statistics */ |
700 | 9 | if (page_buf) { |
701 | 0 | if (type == H5FD_MEM_DRAW) |
702 | 0 | page_buf->bypasses[1]++; |
703 | 0 | else |
704 | 0 | page_buf->bypasses[0]++; |
705 | 0 | } /* end if */ |
706 | | |
707 | | /* If page buffering is disabled, or if this is a large metadata access, |
708 | | * or if this is parallel raw data access, we are done here |
709 | | */ |
710 | 9 | if (NULL == page_buf || (size >= page_buf->page_size && H5FD_MEM_DRAW != type) || |
711 | 9 | (bypass_pb && H5FD_MEM_DRAW == type)) |
712 | 9 | HGOTO_DONE(SUCCEED); |
713 | 9 | } /* end if */ |
714 | | |
715 | | /* Update statistics */ |
716 | 0 | if (page_buf) { |
717 | 0 | if (type == H5FD_MEM_DRAW) |
718 | 0 | page_buf->accesses[1]++; |
719 | 0 | else |
720 | 0 | page_buf->accesses[0]++; |
721 | 0 | } /* end if */ |
722 | | |
723 | | /* Calculate the aligned address of the first page */ |
724 | 0 | first_page_addr = (addr / page_buf->page_size) * page_buf->page_size; |
725 | | |
726 | | /* For Raw data calculate the aligned address of the last page and |
727 | | * the number of pages accessed if more than 1 page is accessed |
728 | | */ |
729 | 0 | if (H5FD_MEM_DRAW == type) { |
730 | 0 | last_page_addr = ((addr + size - 1) / page_buf->page_size) * page_buf->page_size; |
731 | | |
732 | | /* How many pages does this read span */ |
733 | 0 | num_touched_pages = |
734 | 0 | (last_page_addr / page_buf->page_size + 1) - (first_page_addr / page_buf->page_size); |
735 | 0 | if (first_page_addr == last_page_addr) { |
736 | 0 | assert(1 == num_touched_pages); |
737 | 0 | last_page_addr = HADDR_UNDEF; |
738 | 0 | } /* end if */ |
739 | 0 | } /* end if */ |
740 | | /* Otherwise set last page addr to HADDR_UNDEF */ |
741 | 0 | else { |
742 | 0 | num_touched_pages = 1; |
743 | 0 | last_page_addr = HADDR_UNDEF; |
744 | 0 | } /* end else */ |
745 | | |
746 | | /* Translate to file driver I/O info object */ |
747 | 0 | file = f_sh->lf; |
748 | | |
749 | | /* Copy raw data from dirty pages into the read buffer if the read |
750 | | request spans pages in the page buffer*/ |
751 | 0 | if (H5FD_MEM_DRAW == type && size >= page_buf->page_size) { |
752 | 0 | H5SL_node_t *node; |
753 | | |
754 | | /* For each touched page in the page buffer, check if it |
755 | | * exists in the page Buffer and is dirty. If it does, we |
756 | | * update the buffer with what's in the page so we get the up |
757 | | * to date data into the buffer after the big read from the file. |
758 | | */ |
759 | 0 | node = H5SL_find(page_buf->slist_ptr, (void *)(&first_page_addr)); |
760 | 0 | for (i = 0; i < num_touched_pages; i++) { |
761 | 0 | search_addr = i * page_buf->page_size + first_page_addr; |
762 | | |
763 | | /* if we still haven't located a starting page, search again */ |
764 | 0 | if (!node && i != 0) |
765 | 0 | node = H5SL_find(page_buf->slist_ptr, (void *)(&search_addr)); |
766 | | |
767 | | /* if the current page is in the Page Buffer, do the updates */ |
768 | 0 | if (node) { |
769 | 0 | page_entry = (H5PB_entry_t *)H5SL_item(node); |
770 | |
|
771 | 0 | assert(page_entry); |
772 | | |
773 | | /* If the current page address falls out of the access |
774 | | block, then there are no more pages to go over */ |
775 | 0 | if (page_entry->addr >= addr + size) |
776 | 0 | break; |
777 | | |
778 | 0 | assert(page_entry->addr == search_addr); |
779 | |
|
780 | 0 | if (page_entry->is_dirty) { |
781 | | /* special handling for the first page if it is not a full page access */ |
782 | 0 | if (i == 0 && first_page_addr != addr) { |
783 | 0 | offset = addr - first_page_addr; |
784 | 0 | assert(page_buf->page_size > offset); |
785 | |
|
786 | 0 | H5MM_memcpy(buf, (uint8_t *)page_entry->page_buf_ptr + offset, |
787 | 0 | page_buf->page_size - (size_t)offset); |
788 | | |
789 | | /* move to top of LRU list */ |
790 | 0 | H5PB__MOVE_TO_TOP_LRU(page_buf, page_entry) |
791 | 0 | } /* end if */ |
792 | | /* special handling for the last page if it is not a full page access */ |
793 | 0 | else if (num_touched_pages > 1 && i == num_touched_pages - 1 && |
794 | 0 | search_addr < addr + size) { |
795 | 0 | offset = (num_touched_pages - 2) * page_buf->page_size + |
796 | 0 | (page_buf->page_size - (addr - first_page_addr)); |
797 | |
|
798 | 0 | H5MM_memcpy((uint8_t *)buf + offset, page_entry->page_buf_ptr, |
799 | 0 | (size_t)((addr + size) - last_page_addr)); |
800 | | |
801 | | /* move to top of LRU list */ |
802 | 0 | H5PB__MOVE_TO_TOP_LRU(page_buf, page_entry) |
803 | 0 | } /* end else-if */ |
804 | | /* copy the entire fully accessed pages */ |
805 | 0 | else { |
806 | 0 | offset = i * page_buf->page_size; |
807 | |
|
808 | 0 | H5MM_memcpy((uint8_t *)buf + (i * page_buf->page_size), page_entry->page_buf_ptr, |
809 | 0 | page_buf->page_size); |
810 | 0 | } /* end else */ |
811 | 0 | } /* end if */ |
812 | 0 | node = H5SL_next(node); |
813 | 0 | } /* end if */ |
814 | 0 | } /* end for */ |
815 | 0 | } /* end if */ |
816 | 0 | else { |
817 | | /* A raw data access could span 1 or 2 PB entries at this point so |
818 | | we need to handle that */ |
819 | 0 | assert(1 == num_touched_pages || 2 == num_touched_pages); |
820 | 0 | for (i = 0; i < num_touched_pages; i++) { |
821 | 0 | haddr_t buf_offset; |
822 | | |
823 | | /* Calculate the aligned address of the page to search for it in the skip list */ |
824 | 0 | search_addr = (0 == i ? first_page_addr : last_page_addr); |
825 | | |
826 | | /* Calculate the access size if the access spans more than 1 page */ |
827 | 0 | if (1 == num_touched_pages) |
828 | 0 | access_size = size; |
829 | 0 | else |
830 | 0 | access_size = (0 == i ? (size_t)((first_page_addr + page_buf->page_size) - addr) |
831 | 0 | : (size - access_size)); |
832 | | |
833 | | /* Lookup the page in the skip list */ |
834 | 0 | page_entry = (H5PB_entry_t *)H5SL_search(page_buf->slist_ptr, (void *)(&search_addr)); |
835 | | |
836 | | /* if found */ |
837 | 0 | if (page_entry) { |
838 | 0 | offset = (0 == i ? addr - page_entry->addr : 0); |
839 | 0 | buf_offset = (0 == i ? 0 : size - access_size); |
840 | | |
841 | | /* Account for reads that would overflow a page */ |
842 | 0 | if (offset + access_size > page_buf->page_size) |
843 | 0 | access_size = page_buf->page_size - offset; |
844 | | |
845 | | /* copy the requested data from the page into the input buffer */ |
846 | 0 | H5MM_memcpy((uint8_t *)buf + buf_offset, (uint8_t *)page_entry->page_buf_ptr + offset, |
847 | 0 | access_size); |
848 | | |
849 | | /* Update LRU */ |
850 | 0 | H5PB__MOVE_TO_TOP_LRU(page_buf, page_entry) |
851 | | |
852 | | /* Update statistics */ |
853 | 0 | if (type == H5FD_MEM_DRAW) |
854 | 0 | page_buf->hits[1]++; |
855 | 0 | else |
856 | 0 | page_buf->hits[0]++; |
857 | 0 | } /* end if */ |
858 | | /* if not found */ |
859 | 0 | else { |
860 | 0 | void *new_page_buf = NULL; |
861 | 0 | size_t page_size = page_buf->page_size; |
862 | 0 | haddr_t eoa; |
863 | | |
864 | | /* make space for new entry */ |
865 | 0 | if ((H5SL_count(page_buf->slist_ptr) * page_buf->page_size) >= page_buf->max_size) { |
866 | 0 | htri_t can_make_space; |
867 | | |
868 | | /* check if we can make space in page buffer */ |
869 | 0 | if ((can_make_space = H5PB__make_space(f_sh, page_buf, type)) < 0) |
870 | 0 | HGOTO_ERROR(H5E_PAGEBUF, H5E_NOSPACE, FAIL, "make space in Page buffer Failed"); |
871 | | |
872 | | /* if make_space returns 0, then we can't use the page |
873 | | buffer for this I/O and we need to bypass */ |
874 | 0 | if (0 == can_make_space) { |
875 | | /* make space can't return false on second touched page since the first is of the same |
876 | | * type */ |
877 | 0 | assert(0 == i); |
878 | | |
879 | | /* read entire block from VFD and return */ |
880 | 0 | if (H5FD_read(file, type, addr, size, buf) < 0) |
881 | 0 | HGOTO_ERROR(H5E_PAGEBUF, H5E_READERROR, FAIL, "driver read request failed"); |
882 | | |
883 | | /* Break out of loop */ |
884 | 0 | break; |
885 | 0 | } /* end if */ |
886 | 0 | } /* end if */ |
887 | | |
888 | | /* Read page from VFD */ |
889 | 0 | if (NULL == (new_page_buf = H5FL_FAC_MALLOC(page_buf->page_fac))) |
890 | 0 | HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTALLOC, FAIL, |
891 | 0 | "memory allocation failed for page buffer entry"); |
892 | | |
893 | | /* Read page through the VFD layer, but make sure we don't read past the EOA. */ |
894 | | |
895 | | /* Retrieve the 'eoa' for the file */ |
896 | 0 | if (HADDR_UNDEF == (eoa = H5F_shared_get_eoa(f_sh, type))) |
897 | 0 | HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTGET, FAIL, "driver get_eoa request failed"); |
898 | | |
899 | | /* If the entire page falls outside the EOA, then fail */ |
900 | 0 | if (search_addr > eoa) |
901 | 0 | HGOTO_ERROR(H5E_PAGEBUF, H5E_BADVALUE, FAIL, |
902 | 0 | "reading an entire page that is outside the file EOA"); |
903 | | |
904 | | /* Adjust the read size to not go beyond the EOA */ |
905 | 0 | if (search_addr + page_size > eoa) |
906 | 0 | page_size = (size_t)(eoa - search_addr); |
907 | | |
908 | | /* Read page from VFD */ |
909 | 0 | if (H5FD_read(file, type, search_addr, page_size, new_page_buf) < 0) |
910 | 0 | HGOTO_ERROR(H5E_PAGEBUF, H5E_READERROR, FAIL, "driver read request failed"); |
911 | | |
912 | | /* Copy the requested data from the page into the input buffer */ |
913 | 0 | offset = (0 == i ? addr - search_addr : 0); |
914 | 0 | buf_offset = (0 == i ? 0 : size - access_size); |
915 | | |
916 | | /* Account for reads that would overflow a page */ |
917 | 0 | if (offset + access_size > page_buf->page_size) |
918 | 0 | access_size = page_buf->page_size - offset; |
919 | |
|
920 | 0 | H5MM_memcpy((uint8_t *)buf + buf_offset, (uint8_t *)new_page_buf + offset, access_size); |
921 | | |
922 | | /* Create the new PB entry */ |
923 | 0 | if (NULL == (page_entry = H5FL_CALLOC(H5PB_entry_t))) |
924 | 0 | HGOTO_ERROR(H5E_PAGEBUF, H5E_NOSPACE, FAIL, "memory allocation failed"); |
925 | | |
926 | 0 | page_entry->page_buf_ptr = new_page_buf; |
927 | 0 | page_entry->addr = search_addr; |
928 | 0 | page_entry->type = (H5F_mem_page_t)type; |
929 | 0 | page_entry->is_dirty = false; |
930 | | |
931 | | /* Insert page into PB */ |
932 | 0 | if (H5PB__insert_entry(page_buf, page_entry) < 0) |
933 | 0 | HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTSET, FAIL, "error inserting new page in page buffer"); |
934 | | |
935 | | /* Update statistics */ |
936 | 0 | if (type == H5FD_MEM_DRAW) |
937 | 0 | page_buf->misses[1]++; |
938 | 0 | else |
939 | 0 | page_buf->misses[0]++; |
940 | 0 | } /* end else */ |
941 | 0 | } /* end for */ |
942 | 0 | } /* end else */ |
943 | | |
944 | 9 | done: |
945 | 9 | FUNC_LEAVE_NOAPI(ret_value) |
946 | 9 | } /* end H5PB_read() */ |
947 | | |
948 | | /*------------------------------------------------------------------------- |
949 | | * Function: H5PB_write |
950 | | * |
951 | | * Purpose: Write data into the Page Buffer. If the page exists in the |
952 | | * cache, update it; otherwise read it from disk, update it, and |
953 | | * insert into cache. |
954 | | * |
955 | | * Return: Non-negative on success/Negative on failure |
956 | | * |
957 | | *------------------------------------------------------------------------- |
958 | | */ |
959 | | herr_t |
960 | | H5PB_write(H5F_shared_t *f_sh, H5FD_mem_t type, haddr_t addr, size_t size, const void *buf) |
961 | 3 | { |
962 | 3 | H5PB_t *page_buf; /* Page buffering info for this file */ |
963 | 3 | H5PB_entry_t *page_entry; /* Pointer to the corresponding page entry */ |
964 | 3 | H5FD_t *file; /* File driver pointer */ |
965 | 3 | haddr_t first_page_addr, last_page_addr; /* Addresses of the first and last pages covered by I/O */ |
966 | 3 | haddr_t offset; |
967 | 3 | haddr_t search_addr; /* Address of current page */ |
968 | 3 | hsize_t num_touched_pages; /* Number of pages accessed */ |
969 | 3 | size_t access_size = 0; |
970 | 3 | bool bypass_pb = false; /* Whether to bypass page buffering */ |
971 | 3 | hsize_t i; /* Local index variable */ |
972 | 3 | herr_t ret_value = SUCCEED; /* Return value */ |
973 | | |
974 | 3 | FUNC_ENTER_NOAPI(FAIL) |
975 | | |
976 | | /* Sanity checks */ |
977 | 3 | assert(f_sh); |
978 | | |
979 | | /* Get pointer to page buffer info for this file */ |
980 | 3 | page_buf = f_sh->page_buf; |
981 | | |
982 | | #ifdef H5_HAVE_PARALLEL |
983 | | if (H5F_SHARED_HAS_FEATURE(f_sh, H5FD_FEAT_HAS_MPI)) { |
984 | | #if 1 |
985 | | bypass_pb = true; |
986 | | #else |
987 | | /* MSC - why this stopped working ? */ |
988 | | int mpi_size; |
989 | | |
990 | | if ((mpi_size = H5F_shared_mpi_get_size(f_sh)) < 0) |
991 | | HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTGET, FAIL, "can't retrieve MPI communicator size"); |
992 | | if (1 != mpi_size) |
993 | | bypass_pb = true; |
994 | | #endif |
995 | | } /* end if */ |
996 | | #endif |
997 | | |
998 | | /* If page buffering is disabled, or the I/O size is larger than that of a |
999 | | * single page, or if this is a parallel raw data access, bypass page |
1000 | | * buffering. |
1001 | | */ |
1002 | 3 | if (NULL == page_buf || size >= page_buf->page_size || bypass_pb) { |
1003 | 3 | if (H5F__accum_write(f_sh, type, addr, size, buf) < 0) |
1004 | 1 | HGOTO_ERROR(H5E_PAGEBUF, H5E_WRITEERROR, FAIL, "write through metadata accumulator failed"); |
1005 | | |
1006 | | /* Update statistics */ |
1007 | 2 | if (page_buf) { |
1008 | 0 | if (type == H5FD_MEM_DRAW || type == H5FD_MEM_GHEAP) |
1009 | 0 | page_buf->bypasses[1]++; |
1010 | 0 | else |
1011 | 0 | page_buf->bypasses[0]++; |
1012 | 0 | } /* end if */ |
1013 | | |
1014 | | /* If page buffering is disabled, or if this is a large metadata access, |
1015 | | * or if this is a parallel raw data access, we are done here |
1016 | | */ |
1017 | 2 | if (NULL == page_buf || (size >= page_buf->page_size && H5FD_MEM_DRAW != type) || |
1018 | 2 | (bypass_pb && H5FD_MEM_DRAW == type)) |
1019 | 2 | HGOTO_DONE(SUCCEED); |
1020 | | |
1021 | | #ifdef H5_HAVE_PARALLEL |
1022 | | if (bypass_pb) { |
1023 | | if (H5PB_update_entry(page_buf, addr, size, buf) > 0) |
1024 | | HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTUPDATE, FAIL, "failed to update PB with metadata cache"); |
1025 | | HGOTO_DONE(SUCCEED); |
1026 | | } /* end if */ |
1027 | | #endif |
1028 | 2 | } /* end if */ |
1029 | | |
1030 | | /* Update statistics */ |
1031 | 0 | if (page_buf) { |
1032 | 0 | if (type == H5FD_MEM_DRAW || type == H5FD_MEM_GHEAP) |
1033 | 0 | page_buf->accesses[1]++; |
1034 | 0 | else |
1035 | 0 | page_buf->accesses[0]++; |
1036 | 0 | } /* end if */ |
1037 | | |
1038 | | /* Calculate the aligned address of the first page */ |
1039 | 0 | first_page_addr = (addr / page_buf->page_size) * page_buf->page_size; |
1040 | | |
1041 | | /* For raw data calculate the aligned address of the last page and |
1042 | | * the number of pages accessed if more than 1 page is accessed |
1043 | | */ |
1044 | 0 | if (H5FD_MEM_DRAW == type) { |
1045 | 0 | last_page_addr = (addr + size - 1) / page_buf->page_size * page_buf->page_size; |
1046 | | |
1047 | | /* how many pages does this write span */ |
1048 | 0 | num_touched_pages = |
1049 | 0 | (last_page_addr / page_buf->page_size + 1) - (first_page_addr / page_buf->page_size); |
1050 | 0 | if (first_page_addr == last_page_addr) { |
1051 | 0 | assert(1 == num_touched_pages); |
1052 | 0 | last_page_addr = HADDR_UNDEF; |
1053 | 0 | } /* end if */ |
1054 | 0 | } /* end if */ |
1055 | | /* Otherwise set last page addr to HADDR_UNDEF */ |
1056 | 0 | else { |
1057 | 0 | num_touched_pages = 1; |
1058 | 0 | last_page_addr = HADDR_UNDEF; |
1059 | 0 | } /* end else */ |
1060 | | |
1061 | | /* Translate to file driver I/O info object */ |
1062 | 0 | file = f_sh->lf; |
1063 | | |
1064 | | /* Check if existing pages for raw data need to be updated since raw data access is not atomic */ |
1065 | 0 | if (H5FD_MEM_DRAW == type && size >= page_buf->page_size) { |
1066 | | /* For each touched page, check if it exists in the page buffer, and |
1067 | | * update it with the data in the buffer to keep it up to date |
1068 | | */ |
1069 | 0 | for (i = 0; i < num_touched_pages; i++) { |
1070 | 0 | search_addr = i * page_buf->page_size + first_page_addr; |
1071 | | |
1072 | | /* Special handling for the first page if it is not a full page update */ |
1073 | 0 | if (i == 0 && first_page_addr != addr) { |
1074 | | /* Lookup the page in the skip list */ |
1075 | 0 | page_entry = (H5PB_entry_t *)H5SL_search(page_buf->slist_ptr, (void *)(&search_addr)); |
1076 | 0 | if (page_entry) { |
1077 | 0 | offset = addr - first_page_addr; |
1078 | 0 | assert(page_buf->page_size > offset); |
1079 | | |
1080 | | /* Update page's data */ |
1081 | 0 | H5MM_memcpy((uint8_t *)page_entry->page_buf_ptr + offset, buf, |
1082 | 0 | page_buf->page_size - (size_t)offset); |
1083 | | |
1084 | | /* Mark page dirty and push to top of LRU */ |
1085 | 0 | page_entry->is_dirty = true; |
1086 | 0 | H5PB__MOVE_TO_TOP_LRU(page_buf, page_entry) |
1087 | 0 | } /* end if */ |
1088 | 0 | } /* end if */ |
1089 | | /* Special handling for the last page if it is not a full page update */ |
1090 | 0 | else if (num_touched_pages > 1 && i == (num_touched_pages - 1) && |
1091 | 0 | (search_addr + page_buf->page_size) != (addr + size)) { |
1092 | 0 | assert(search_addr + page_buf->page_size > addr + size); |
1093 | | |
1094 | | /* Lookup the page in the skip list */ |
1095 | 0 | page_entry = (H5PB_entry_t *)H5SL_search(page_buf->slist_ptr, (void *)(&search_addr)); |
1096 | 0 | if (page_entry) { |
1097 | 0 | offset = (num_touched_pages - 2) * page_buf->page_size + |
1098 | 0 | (page_buf->page_size - (addr - first_page_addr)); |
1099 | | |
1100 | | /* Update page's data */ |
1101 | 0 | H5MM_memcpy(page_entry->page_buf_ptr, (const uint8_t *)buf + offset, |
1102 | 0 | (size_t)((addr + size) - last_page_addr)); |
1103 | | |
1104 | | /* Mark page dirty and push to top of LRU */ |
1105 | 0 | page_entry->is_dirty = true; |
1106 | 0 | H5PB__MOVE_TO_TOP_LRU(page_buf, page_entry) |
1107 | 0 | } /* end if */ |
1108 | 0 | } /* end else-if */ |
1109 | | /* Discard all fully written pages from the page buffer */ |
1110 | 0 | else { |
1111 | 0 | page_entry = (H5PB_entry_t *)H5SL_remove(page_buf->slist_ptr, (void *)(&search_addr)); |
1112 | 0 | if (page_entry) { |
1113 | | /* Remove from LRU list */ |
1114 | 0 | H5PB__REMOVE_LRU(page_buf, page_entry) |
1115 | | |
1116 | | /* Decrement page count of appropriate type */ |
1117 | 0 | if (H5F_MEM_PAGE_DRAW == page_entry->type || H5F_MEM_PAGE_GHEAP == page_entry->type) |
1118 | 0 | page_buf->raw_count--; |
1119 | 0 | else |
1120 | 0 | page_buf->meta_count--; |
1121 | | |
1122 | | /* Free page info */ |
1123 | 0 | page_entry->page_buf_ptr = H5FL_FAC_FREE(page_buf->page_fac, page_entry->page_buf_ptr); |
1124 | 0 | page_entry = H5FL_FREE(H5PB_entry_t, page_entry); |
1125 | 0 | } /* end if */ |
1126 | 0 | } /* end else */ |
1127 | 0 | } /* end for */ |
1128 | 0 | } /* end if */ |
1129 | 0 | else { |
1130 | | /* An access could span 1 or 2 PBs at this point so we need to handle that */ |
1131 | 0 | assert(1 == num_touched_pages || 2 == num_touched_pages); |
1132 | 0 | for (i = 0; i < num_touched_pages; i++) { |
1133 | 0 | haddr_t buf_offset; |
1134 | | |
1135 | | /* Calculate the aligned address of the page to search for it in the skip list */ |
1136 | 0 | search_addr = (0 == i ? first_page_addr : last_page_addr); |
1137 | | |
1138 | | /* Calculate the access size if the access spans more than 1 page */ |
1139 | 0 | if (1 == num_touched_pages) |
1140 | 0 | access_size = size; |
1141 | 0 | else |
1142 | 0 | access_size = |
1143 | 0 | (0 == i ? (size_t)(first_page_addr + page_buf->page_size - addr) : (size - access_size)); |
1144 | | |
1145 | | /* Lookup the page in the skip list */ |
1146 | 0 | page_entry = (H5PB_entry_t *)H5SL_search(page_buf->slist_ptr, (void *)(&search_addr)); |
1147 | | |
1148 | | /* If found */ |
1149 | 0 | if (page_entry) { |
1150 | 0 | offset = (0 == i ? addr - page_entry->addr : 0); |
1151 | 0 | buf_offset = (0 == i ? 0 : size - access_size); |
1152 | | |
1153 | | /* Copy the requested data from the input buffer into the page */ |
1154 | 0 | H5MM_memcpy((uint8_t *)page_entry->page_buf_ptr + offset, (const uint8_t *)buf + buf_offset, |
1155 | 0 | access_size); |
1156 | | |
1157 | | /* Mark page dirty and push to top of LRU */ |
1158 | 0 | page_entry->is_dirty = true; |
1159 | 0 | H5PB__MOVE_TO_TOP_LRU(page_buf, page_entry) |
1160 | | |
1161 | | /* Update statistics */ |
1162 | 0 | if (type == H5FD_MEM_DRAW || type == H5FD_MEM_GHEAP) |
1163 | 0 | page_buf->hits[1]++; |
1164 | 0 | else |
1165 | 0 | page_buf->hits[0]++; |
1166 | 0 | } /* end if */ |
1167 | | /* If not found */ |
1168 | 0 | else { |
1169 | 0 | void *new_page_buf; |
1170 | 0 | size_t page_size = page_buf->page_size; |
1171 | | |
1172 | | /* Make space for new entry */ |
1173 | 0 | if ((H5SL_count(page_buf->slist_ptr) * page_buf->page_size) >= page_buf->max_size) { |
1174 | 0 | htri_t can_make_space; |
1175 | | |
1176 | | /* Check if we can make space in page buffer */ |
1177 | 0 | if ((can_make_space = H5PB__make_space(f_sh, page_buf, type)) < 0) |
1178 | 0 | HGOTO_ERROR(H5E_PAGEBUF, H5E_NOSPACE, FAIL, "make space in Page buffer Failed"); |
1179 | | |
1180 | | /* If make_space returns 0, then we can't use the page |
1181 | | * buffer for this I/O and we need to bypass |
1182 | | */ |
1183 | 0 | if (0 == can_make_space) { |
1184 | 0 | assert(0 == i); |
1185 | | |
1186 | | /* Write to VFD and return */ |
1187 | 0 | if (H5FD_write(file, type, addr, size, buf) < 0) |
1188 | 0 | HGOTO_ERROR(H5E_PAGEBUF, H5E_WRITEERROR, FAIL, "driver write request failed"); |
1189 | | |
1190 | | /* Break out of loop */ |
1191 | 0 | break; |
1192 | 0 | } /* end if */ |
1193 | 0 | } /* end if */ |
1194 | | |
1195 | | /* Don't bother searching if there is no write access */ |
1196 | 0 | if (H5F_ACC_RDWR & H5F_SHARED_INTENT(f_sh)) |
1197 | | /* Lookup & remove the page from the new skip list page if |
1198 | | * it exists to see if this is a new page from the MF layer |
1199 | | */ |
1200 | 0 | page_entry = (H5PB_entry_t *)H5SL_remove(page_buf->mf_slist_ptr, (void *)(&search_addr)); |
1201 | | |
1202 | | /* Calculate offset into the buffer of the page and the user buffer */ |
1203 | 0 | offset = (0 == i ? addr - search_addr : 0); |
1204 | 0 | buf_offset = (0 == i ? 0 : size - access_size); |
1205 | | |
1206 | | /* If found, then just update the buffer pointer to the newly allocate buffer */ |
1207 | 0 | if (page_entry) { |
1208 | | /* Allocate space for the page buffer */ |
1209 | 0 | if (NULL == (new_page_buf = H5FL_FAC_MALLOC(page_buf->page_fac))) |
1210 | 0 | HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTALLOC, FAIL, |
1211 | 0 | "memory allocation failed for page buffer entry"); |
1212 | 0 | memset(new_page_buf, 0, (size_t)offset); |
1213 | 0 | memset((uint8_t *)new_page_buf + offset + access_size, 0, |
1214 | 0 | page_size - ((size_t)offset + access_size)); |
1215 | |
|
1216 | 0 | page_entry->page_buf_ptr = new_page_buf; |
1217 | | |
1218 | | /* Update statistics */ |
1219 | 0 | if (type == H5FD_MEM_DRAW || type == H5FD_MEM_GHEAP) |
1220 | 0 | page_buf->hits[1]++; |
1221 | 0 | else |
1222 | 0 | page_buf->hits[0]++; |
1223 | 0 | } /* end if */ |
1224 | | /* Otherwise read page through the VFD layer, but make sure we don't read past the EOA. */ |
1225 | 0 | else { |
1226 | 0 | haddr_t eoa, eof = HADDR_UNDEF; |
1227 | | |
1228 | | /* Allocate space for the page buffer */ |
1229 | 0 | if (NULL == (new_page_buf = H5FL_FAC_CALLOC(page_buf->page_fac))) |
1230 | 0 | HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTALLOC, FAIL, |
1231 | 0 | "memory allocation failed for page buffer entry"); |
1232 | | |
1233 | | /* Create the new loaded PB entry */ |
1234 | 0 | if (NULL == (page_entry = H5FL_CALLOC(H5PB_entry_t))) |
1235 | 0 | HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTALLOC, FAIL, "memory allocation failed"); |
1236 | | |
1237 | 0 | page_entry->page_buf_ptr = new_page_buf; |
1238 | 0 | page_entry->addr = search_addr; |
1239 | 0 | page_entry->type = (H5F_mem_page_t)type; |
1240 | | |
1241 | | /* Retrieve the 'eoa' for the file */ |
1242 | 0 | if (HADDR_UNDEF == (eoa = H5F_shared_get_eoa(f_sh, type))) |
1243 | 0 | HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTGET, FAIL, "driver get_eoa request failed"); |
1244 | | |
1245 | | /* If the entire page falls outside the EOA, then fail */ |
1246 | 0 | if (search_addr > eoa) |
1247 | 0 | HGOTO_ERROR(H5E_PAGEBUF, H5E_BADVALUE, FAIL, |
1248 | 0 | "writing to a page that is outside the file EOA"); |
1249 | | |
1250 | | /* Retrieve the 'eof' for the file - The MPI-VFD EOF |
1251 | | * returned will most likely be HADDR_UNDEF, so skip |
1252 | | * that check. |
1253 | | */ |
1254 | 0 | if (!H5F_SHARED_HAS_FEATURE(f_sh, H5FD_FEAT_HAS_MPI)) |
1255 | 0 | if (HADDR_UNDEF == (eof = H5FD_get_eof(f_sh->lf, H5FD_MEM_DEFAULT))) |
1256 | 0 | HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTGET, FAIL, "driver get_eof request failed"); |
1257 | | |
1258 | | /* Adjust the read size to not go beyond the EOA */ |
1259 | 0 | if (search_addr + page_size > eoa) |
1260 | 0 | page_size = (size_t)(eoa - search_addr); |
1261 | |
|
1262 | 0 | if (search_addr < eof) { |
1263 | 0 | if (H5FD_read(file, type, search_addr, page_size, new_page_buf) < 0) |
1264 | 0 | HGOTO_ERROR(H5E_PAGEBUF, H5E_READERROR, FAIL, "driver read request failed"); |
1265 | | |
1266 | | /* Update statistics */ |
1267 | 0 | if (type == H5FD_MEM_DRAW || type == H5FD_MEM_GHEAP) |
1268 | 0 | page_buf->misses[1]++; |
1269 | 0 | else |
1270 | 0 | page_buf->misses[0]++; |
1271 | 0 | } /* end if */ |
1272 | 0 | } /* end else */ |
1273 | | |
1274 | | /* Copy the requested data from the page into the input buffer */ |
1275 | 0 | H5MM_memcpy((uint8_t *)new_page_buf + offset, (const uint8_t *)buf + buf_offset, access_size); |
1276 | | |
1277 | | /* Page is dirty now */ |
1278 | 0 | page_entry->is_dirty = true; |
1279 | | |
1280 | | /* Insert page into PB, evicting other pages as necessary */ |
1281 | 0 | if (H5PB__insert_entry(page_buf, page_entry) < 0) |
1282 | 0 | HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTSET, FAIL, "error inserting new page in page buffer"); |
1283 | 0 | } /* end else */ |
1284 | 0 | } /* end for */ |
1285 | 0 | } /* end else */ |
1286 | | |
1287 | 3 | done: |
1288 | 3 | FUNC_LEAVE_NOAPI(ret_value) |
1289 | 3 | } /* end H5PB_write() */ |
1290 | | |
1291 | | /*------------------------------------------------------------------------- |
1292 | | * Function: H5PB_enabled |
1293 | | * |
1294 | | * Purpose: Check if the page buffer may be enabled for the specified |
1295 | | * file and data access type. |
1296 | | * |
1297 | | * Return: Non-negative on success/Negative on failure |
1298 | | * |
1299 | | *------------------------------------------------------------------------- |
1300 | | */ |
1301 | | herr_t |
1302 | | H5PB_enabled(H5F_shared_t *f_sh, H5FD_mem_t type, bool *enabled) |
1303 | 0 | { |
1304 | 0 | H5PB_t *page_buf; /* Page buffering info for this file */ |
1305 | 0 | bool bypass_pb = false; /* Whether to bypass page buffering */ |
1306 | 0 | herr_t ret_value = SUCCEED; /* Return value */ |
1307 | |
|
1308 | 0 | FUNC_ENTER_NOAPI_NOERR |
1309 | | |
1310 | | /* Sanity checks */ |
1311 | 0 | assert(f_sh); |
1312 | | |
1313 | | /* Get pointer to page buffer info for this file */ |
1314 | 0 | page_buf = f_sh->page_buf; |
1315 | |
|
1316 | | #ifdef H5_HAVE_PARALLEL |
1317 | | if (H5F_SHARED_HAS_FEATURE(f_sh, H5FD_FEAT_HAS_MPI)) { |
1318 | | #if 1 |
1319 | | bypass_pb = true; |
1320 | | #else |
1321 | | /* MSC - why this stopped working ? */ |
1322 | | int mpi_size; |
1323 | | |
1324 | | if ((mpi_size = H5F_shared_mpi_get_size(f_sh)) < 0) |
1325 | | HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTGET, FAIL, "can't retrieve MPI communicator size"); |
1326 | | if (1 != mpi_size) |
1327 | | bypass_pb = true; |
1328 | | #endif |
1329 | | } /* end if */ |
1330 | | #endif |
1331 | | |
1332 | | /* If page buffering is disabled, or if this is a parallel raw data access, |
1333 | | * bypass page buffering. Note that page buffering may still be disabled for |
1334 | | * large metadata access or large non-parallel raw data access, but this |
1335 | | * function doesn't take I/O size into account so if it returns true the |
1336 | | * page buffer may still be disabled for some I/O. If it returns false it is |
1337 | | * always disabled for this access type. |
1338 | | */ |
1339 | 0 | if (NULL == page_buf || (bypass_pb && H5FD_MEM_DRAW == type)) { |
1340 | | /* Update statistics, since wherever this function is called, if it |
1341 | | * returns false, the calling function performs I/O avoiding the page |
1342 | | * buffer layer */ |
1343 | 0 | if (page_buf) { |
1344 | 0 | assert(type == H5FD_MEM_DRAW); |
1345 | 0 | page_buf->bypasses[1]++; |
1346 | 0 | } /* end if */ |
1347 | | |
1348 | | /* Page buffer is disabled, at least for this data access type */ |
1349 | 0 | *enabled = false; |
1350 | 0 | } /* end if */ |
1351 | 0 | else |
1352 | | /* Page buffer may be enabled */ |
1353 | 0 | *enabled = true; |
1354 | |
|
1355 | 0 | FUNC_LEAVE_NOAPI(ret_value) |
1356 | 0 | } /* end H5PB_enabled() */ |
1357 | | |
1358 | | /*------------------------------------------------------------------------- |
1359 | | * Function: H5PB__insert_entry() |
1360 | | * |
1361 | | * Purpose: This function was created without documentation. |
1362 | | * What follows is my best understanding of Mohamad's intent. |
1363 | | * |
1364 | | * Insert the supplied page into the page buffer, both the |
1365 | | * skip list and the LRU. |
1366 | | * |
1367 | | * As best I can tell, this function imposes no limit on the |
1368 | | * number of entries in the page buffer beyond an assertion |
1369 | | * failure it the page count exceeds the limit. |
1370 | | * |
1371 | | * JRM -- 12/22/16 |
1372 | | * |
1373 | | * |
1374 | | * Return: Non-negative on success/Negative on failure |
1375 | | * |
1376 | | *------------------------------------------------------------------------- |
1377 | | */ |
1378 | | static herr_t |
1379 | | H5PB__insert_entry(H5PB_t *page_buf, H5PB_entry_t *page_entry) |
1380 | 0 | { |
1381 | 0 | herr_t ret_value = SUCCEED; /* Return value */ |
1382 | |
|
1383 | 0 | FUNC_ENTER_PACKAGE |
1384 | | |
1385 | | /* Insert entry in skip list */ |
1386 | 0 | if (H5SL_insert(page_buf->slist_ptr, page_entry, &(page_entry->addr)) < 0) |
1387 | 0 | HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTINSERT, FAIL, "can't insert entry in skip list"); |
1388 | 0 | assert(H5SL_count(page_buf->slist_ptr) * page_buf->page_size <= page_buf->max_size); |
1389 | | |
1390 | | /* Increment appropriate page count */ |
1391 | 0 | if (H5F_MEM_PAGE_DRAW == page_entry->type || H5F_MEM_PAGE_GHEAP == page_entry->type) |
1392 | 0 | page_buf->raw_count++; |
1393 | 0 | else |
1394 | 0 | page_buf->meta_count++; |
1395 | | |
1396 | | /* Insert entry in LRU */ |
1397 | 0 | H5PB__INSERT_LRU(page_buf, page_entry) |
1398 | |
|
1399 | 0 | done: |
1400 | 0 | FUNC_LEAVE_NOAPI(ret_value) |
1401 | 0 | } /* end H5PB__insert_entry() */ |
1402 | | |
1403 | | /*------------------------------------------------------------------------- |
1404 | | * Function: H5PB__make_space() |
1405 | | * |
1406 | | * Purpose: This function was created without documentation. |
1407 | | * What follows is my best understanding of Mohamad's intent. |
1408 | | * |
1409 | | * If necessary and if possible, evict a page from the page |
1410 | | * buffer to make space for the supplied page. Depending on |
1411 | | * the page buffer configuration and contents, and the page |
1412 | | * supplied this may or may not be possible. |
1413 | | * |
1414 | | * JRM -- 12/22/16 |
1415 | | * |
1416 | | * Return: Non-negative on success/Negative on failure |
1417 | | * |
1418 | | *------------------------------------------------------------------------- |
1419 | | */ |
1420 | | static htri_t |
1421 | | H5PB__make_space(H5F_shared_t *f_sh, H5PB_t *page_buf, H5FD_mem_t inserted_type) |
1422 | 0 | { |
1423 | 0 | H5PB_entry_t *page_entry; /* Pointer to page eviction candidate */ |
1424 | 0 | htri_t ret_value = true; /* Return value */ |
1425 | |
|
1426 | 0 | FUNC_ENTER_PACKAGE |
1427 | | |
1428 | | /* Sanity check */ |
1429 | 0 | assert(f_sh); |
1430 | 0 | assert(page_buf); |
1431 | | |
1432 | | /* Get oldest entry */ |
1433 | 0 | page_entry = page_buf->LRU_tail_ptr; |
1434 | |
|
1435 | 0 | if (H5FD_MEM_DRAW == inserted_type) { |
1436 | | /* If threshould is 100% metadata and page buffer is full of |
1437 | | metadata, then we can't make space for raw data */ |
1438 | 0 | if (0 == page_buf->raw_count && page_buf->min_meta_count == page_buf->meta_count) { |
1439 | 0 | assert(page_buf->meta_count * page_buf->page_size == page_buf->max_size); |
1440 | 0 | HGOTO_DONE(false); |
1441 | 0 | } /* end if */ |
1442 | | |
1443 | | /* check the metadata threshold before evicting metadata items */ |
1444 | 0 | while (1) { |
1445 | 0 | if (page_entry->prev && H5F_MEM_PAGE_META == page_entry->type && |
1446 | 0 | page_buf->min_meta_count >= page_buf->meta_count) |
1447 | 0 | page_entry = page_entry->prev; |
1448 | 0 | else |
1449 | 0 | break; |
1450 | 0 | } /* end while */ |
1451 | 0 | } /* end if */ |
1452 | 0 | else { |
1453 | | /* If threshould is 100% raw data and page buffer is full of |
1454 | | raw data, then we can't make space for meta data */ |
1455 | 0 | if (0 == page_buf->meta_count && page_buf->min_raw_count == page_buf->raw_count) { |
1456 | 0 | assert(page_buf->raw_count * page_buf->page_size == page_buf->max_size); |
1457 | 0 | HGOTO_DONE(false); |
1458 | 0 | } /* end if */ |
1459 | | |
1460 | | /* check the raw data threshold before evicting raw data items */ |
1461 | 0 | while (1) { |
1462 | 0 | if (page_entry->prev && |
1463 | 0 | (H5F_MEM_PAGE_DRAW == page_entry->type || H5F_MEM_PAGE_GHEAP == page_entry->type) && |
1464 | 0 | page_buf->min_raw_count >= page_buf->raw_count) |
1465 | 0 | page_entry = page_entry->prev; |
1466 | 0 | else |
1467 | 0 | break; |
1468 | 0 | } /* end while */ |
1469 | 0 | } /* end else */ |
1470 | | |
1471 | | /* Remove from page index */ |
1472 | 0 | if (NULL == H5SL_remove(page_buf->slist_ptr, &(page_entry->addr))) |
1473 | 0 | HGOTO_ERROR(H5E_PAGEBUF, H5E_BADVALUE, FAIL, "Tail Page Entry is not in skip list"); |
1474 | | |
1475 | | /* Remove entry from LRU list */ |
1476 | 0 | H5PB__REMOVE_LRU(page_buf, page_entry) |
1477 | 0 | assert(H5SL_count(page_buf->slist_ptr) == page_buf->LRU_list_len); |
1478 | | |
1479 | | /* Decrement appropriate page type counter */ |
1480 | 0 | if (H5F_MEM_PAGE_DRAW == page_entry->type || H5F_MEM_PAGE_GHEAP == page_entry->type) |
1481 | 0 | page_buf->raw_count--; |
1482 | 0 | else |
1483 | 0 | page_buf->meta_count--; |
1484 | | |
1485 | | /* Flush page if dirty */ |
1486 | 0 | if (page_entry->is_dirty) |
1487 | 0 | if (H5PB__write_entry(f_sh, page_entry) < 0) |
1488 | 0 | HGOTO_ERROR(H5E_PAGEBUF, H5E_WRITEERROR, FAIL, "file write failed"); |
1489 | | |
1490 | | /* Update statistics */ |
1491 | 0 | if (page_entry->type == H5F_MEM_PAGE_DRAW || H5F_MEM_PAGE_GHEAP == page_entry->type) |
1492 | 0 | page_buf->evictions[1]++; |
1493 | 0 | else |
1494 | 0 | page_buf->evictions[0]++; |
1495 | | |
1496 | | /* Release page */ |
1497 | 0 | page_entry->page_buf_ptr = H5FL_FAC_FREE(page_buf->page_fac, page_entry->page_buf_ptr); |
1498 | 0 | page_entry = H5FL_FREE(H5PB_entry_t, page_entry); |
1499 | |
|
1500 | 0 | done: |
1501 | 0 | FUNC_LEAVE_NOAPI(ret_value) |
1502 | 0 | } /* end H5PB__make_space() */ |
1503 | | |
1504 | | /*------------------------------------------------------------------------- |
1505 | | * Function: H5PB__write_entry() |
1506 | | * |
1507 | | * Purpose: ??? |
1508 | | * |
1509 | | * This function was created without documentation. |
1510 | | * What follows is my best understanding of Mohamad's intent. |
1511 | | * |
1512 | | * Return: Non-negative on success/Negative on failure |
1513 | | * |
1514 | | *------------------------------------------------------------------------- |
1515 | | */ |
1516 | | static herr_t |
1517 | | H5PB__write_entry(H5F_shared_t *f_sh, H5PB_entry_t *page_entry) |
1518 | 0 | { |
1519 | 0 | haddr_t eoa; /* Current EOA for the file */ |
1520 | 0 | herr_t ret_value = SUCCEED; /* Return value */ |
1521 | |
|
1522 | 0 | FUNC_ENTER_PACKAGE |
1523 | | |
1524 | | /* Sanity check */ |
1525 | 0 | assert(f_sh); |
1526 | 0 | assert(page_entry); |
1527 | | |
1528 | | /* Retrieve the 'eoa' for the file */ |
1529 | 0 | if (HADDR_UNDEF == (eoa = H5F_shared_get_eoa(f_sh, (H5FD_mem_t)page_entry->type))) |
1530 | 0 | HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTGET, FAIL, "driver get_eoa request failed"); |
1531 | | |
1532 | | /* If the starting address of the page is larger than |
1533 | | * the EOA, then the entire page is discarded without writing. |
1534 | | */ |
1535 | 0 | if (page_entry->addr <= eoa) { |
1536 | 0 | H5FD_t *file; /* File driver I/O info */ |
1537 | 0 | size_t page_size = f_sh->page_buf->page_size; |
1538 | | |
1539 | | /* Adjust the page length if it exceeds the EOA */ |
1540 | 0 | if ((page_entry->addr + page_size) > eoa) |
1541 | 0 | page_size = (size_t)(eoa - page_entry->addr); |
1542 | | |
1543 | | /* Translate to file driver I/O info object */ |
1544 | 0 | file = f_sh->lf; |
1545 | |
|
1546 | 0 | if (H5FD_write(file, (H5FD_mem_t)page_entry->type, page_entry->addr, page_size, |
1547 | 0 | page_entry->page_buf_ptr) < 0) |
1548 | 0 | HGOTO_ERROR(H5E_PAGEBUF, H5E_WRITEERROR, FAIL, "file write failed"); |
1549 | 0 | } /* end if */ |
1550 | | |
1551 | 0 | page_entry->is_dirty = false; |
1552 | |
|
1553 | 0 | done: |
1554 | 0 | FUNC_LEAVE_NOAPI(ret_value) |
1555 | 0 | } /* end H5PB__write_entry() */ |