Coverage Report

Created: 2025-08-26 06:30

/src/hdf5/src/H5PB.c
Line
Count
Source (jump to first uncovered line)
1
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2
 * Copyright by The HDF Group.                                               *
3
 * All rights reserved.                                                      *
4
 *                                                                           *
5
 * This file is part of HDF5.  The full HDF5 copyright notice, including     *
6
 * terms governing use, modification, and redistribution, is contained in    *
7
 * the LICENSE file, which can be found at the root of the source code       *
8
 * distribution tree, or in https://www.hdfgroup.org/licenses.               *
9
 * If you do not have access to either file, you may request a copy from     *
10
 * help@hdfgroup.org.                                                        *
11
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
12
13
/*-------------------------------------------------------------------------
14
 *
15
 * Created:             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() */